Objectives
In this section we will learn how to generate graphics in R.
The material presented is here largely based on the material presented in:
Install and load packages
# Clear the workspace
rm(list = ls())
# List of package needed for this workshop
reqpkg <- c("ggplot2", "ggrepel", "ggthemes", "grid", "gridExtra",
"RColorBrewer")
# Check if the packages are installed:
inpkg = installed.packages()[, "Package"] #installed packages
neededpkg = reqpkg[!reqpkg %in% inpkg]
if(length(neededpkg) > 0){
stop(paste("\n Need to install the following package:", neededpkg))
}
# Load all required packages and show version
for(i in reqpkg){
print(paste(i, "version:", packageVersion(i)))
library(i, quietly=TRUE, verbose=FALSE, warn.conflicts=FALSE, character.only=TRUE)
}
[1] "ggplot2 version: 2.1.0"
[1] "ggrepel version: 0.5.1"
[1] "ggthemes version: 3.2.0"
[1] "grid version: 3.3.0"
[1] "gridExtra version: 2.2.1"
[1] "RColorBrewer version: 1.1.2"
If you haven’t done that already, download the workshop materials:
setwd("/path/to/dir/")
download.file("/url/to/workshop/materials", "R_workshop.zip")
unzip("R_workshop.zip")
Introduction
Instructions:
- Ask any question any time if anything is unclear!
- Collaboration is encouraged. Ask a friend! They might dealt with the same issue just a minute ago.
- Each of you should have 2 post-its. Use them to attach to you laptop.
- red – if you can’t figure out a task
- green – everything is good
This course assumes that you:
- have some experience with R already (e.g. the previous 2 workshop sections)
- would like to learn how to create cool visualization R without going much into detail of what computations are done on the side.
What is ggplot2?
ggplot2 is a plotting system for R, based on the grammar of graphics. It takes care of many of the fiddly details that make plotting a hassle (like drawing legends) as well as providing a powerful model of graphics that makes it easy to produce complex multi-layered graphics.
Advantages of ggplot2:
- plots are defined at a high level of abstraction,
- plots are broken down into modules/layers
- a great flexibility when customizing your plot
- good documentation
- a large user base (easy access to help)
Weaknesses of ggplot2 (what the package should not be used for):
- 3D graphics: see
rgl package instead or ggplot2 + plotly
- graph/network plots with nodes and edges: see
igraph package instead
- interactive graphics: see
ggvis, plotly packages instead
What is the grammar of graphics?
It is a concept coined by Leland Wilkinson in 2005.
The basic idea: a plot is defined by independent building blocks, which combined create just about any kind of visualization you want.
The building blocks of a graph include:
- data
- aesthetic mapping
- geometric objects
- statistical transformations
- scales
- coordinate system
- positioning adjustments
- facteing
The structure of ggplot object
The ggplot() function is used to initialize the basic graph structure. It cannot produce the plot we want by itself. Instead, we need to add extra building blocks it. The structure of a ggplot looks like this:
ggplot(data = <default data set>,
aes(x = <default x axis variable>,
y = <default y axis variable>,
... <other default aesthetic mappings>),
... <other plot defaults>) +
geom_<geom type>(aes(size = <size variable for this geom>,
... <other aesthetic mappings>),
data = <data for this point geom>,
stat = <statistic string or function>,
position = <position string or function>,
color = <"fixed color specification">,
... <other arguments, possibly passed to the _stat_ function) +
scale_<aesthetic>_<type>(name = <"scale label">,
breaks = <where to put tick marks>,
labels = <labels for tick marks>,
... <other options for the scale>) +
theme(plot.background = element_rect(fill = "gray"),
... <other theme elements>)
This chunk of code might seem confusing, but by the end of this workshop you should be able to understand each of the components.
The basic idea is that you specify different parts of the plot, and add them together using the + operator.
ggplot2 vs base graphics:
ggplot2 compared to base graphics is:
- more verbose for simple / out of the box graphics
- less verbose for complex / custom graphics
- uses a different system for adding plot elements (
+ adding operator instead of calling new functions like points(), lines() etc.)
Example 1: History of unemployemnt
data("economics")
str(economics)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 574 obs. of 6 variables:
$ date : Date, format: "1967-07-01" "1967-08-01" "1967-09-01" "1967-10-01" ...
$ pce : num 507 510 516 513 518 ...
$ pop : int 198712 198911 199113 199311 199498 199657 199808 199920 200056 200208 ...
$ psavert : num 12.5 12.5 11.7 12.5 12.5 12.1 11.7 12.2 11.6 12.2 ...
$ uempmed : num 4.5 4.7 4.6 4.9 4.7 4.8 5.1 4.5 4.1 4.6 ...
$ unemploy: int 2944 2945 2958 3143 3066 3018 2878 3001 2877 2709 ...
head(economics)
Source: local data frame [6 x 6]
date pce pop psavert uempmed unemploy
<date> <dbl> <int> <dbl> <dbl> <int>
1 1967-07-01 507.4 198712 12.5 4.5 2944
2 1967-08-01 510.5 198911 12.5 4.7 2945
3 1967-09-01 516.3 199113 11.7 4.6 2958
4 1967-10-01 512.9 199311 12.5 4.9 3143
5 1967-11-01 518.1 199498 12.5 4.7 3066
6 1967-12-01 525.8 199657 12.1 4.8 3018
Base graphics with a simple plot() function:
plot(unemploy/pop ~ date, data = economics, type = "l")

A similar plot using ggplot2
ggplot(data = economics, aes(x = date, y = unemploy/pop)) + geom_line()

Note that, ggplot() by itself does not plot the data.
ggplot(data = economics, aes(x = date, y = unemploy/pop))

You need to add the lines object.
ggplot(data = economics, aes(x = date, y = unemploy/pop)) + geom_line()

… and possibly change the background color from default gray to customized white.
ggplot(data = economics, aes(x = date, y = unemploy/pop)) + geom_line() +
theme_bw()

What if we want to compare the trend from year 2009 to 2014?
Add two variables one for year and one for day of the year:
economics$dayOftheYear <- format(economics$date, format="%m-%d")
economics$dayOftheYear <- as.Date(economics$dayOftheYear, format="%m-%d")
economics$year <- format(economics$date, format="%Y")
head(economics)
Source: local data frame [6 x 8]
date pce pop psavert uempmed unemploy dayOftheYear year
<date> <dbl> <int> <dbl> <dbl> <int> <date> <chr>
1 1967-07-01 507.4 198712 12.5 4.5 2944 2016-07-01 1967
2 1967-08-01 510.5 198911 12.5 4.7 2945 2016-08-01 1967
3 1967-09-01 516.3 199113 11.7 4.6 2958 2016-09-01 1967
4 1967-10-01 512.9 199311 12.5 4.9 3143 2016-10-01 1967
5 1967-11-01 518.1 199498 12.5 4.7 3066 2016-11-01 1967
6 1967-12-01 525.8 199657 12.1 4.8 3018 2016-12-01 1967
Using base graphics:
plot(unemploy/pop ~ dayOftheYear, data = subset(economics, year == 2009),
ylim = c(0.025, 0.05), type = "l")
lines(unemploy/pop ~ dayOftheYear, col = "red", data = subset(economics, year == 2014))
legend("topleft",
c("2008", "2014"), title="Year",
col=c("black", "red"),
pch=c(1, 1))

Using ggplot2:
ggplot(data = subset(economics, year %in% c(2014, 2009)),
aes(x = dayOftheYear, y = unemploy/pop)) +
geom_line(aes(color = year))

Note that there is no need to specify the legend! It is produced automatically in ggplot2.
It is easy to even plot all the years together:
ggplot(data = economics, aes(x = dayOftheYear, y = unemploy/pop)) +
geom_line(aes(color = year))

Example 2: diamonds dataset
We will now load a diamonds data set that is included in with the ggplot2 package.
The data set contains the prices and other attributes of almost 54,000 diamonds. You can call ?diamonds to learn more about the available attributes.
data("diamonds")
str(diamonds)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 53940 obs. of 10 variables:
$ carat : num 0.23 0.21 0.23 0.29 0.31 0.24 0.24 0.26 0.22 0.23 ...
$ cut : Ord.factor w/ 5 levels "Fair"<"Good"<..: 5 4 2 4 2 3 3 3 1 3 ...
$ color : Ord.factor w/ 7 levels "D"<"E"<"F"<"G"<..: 2 2 2 6 7 7 6 5 2 5 ...
$ clarity: Ord.factor w/ 8 levels "I1"<"SI2"<"SI1"<..: 2 3 5 4 2 6 7 3 4 5 ...
$ depth : num 61.5 59.8 56.9 62.4 63.3 62.8 62.3 61.9 65.1 59.4 ...
$ table : num 55 61 65 58 58 57 57 55 61 61 ...
$ price : int 326 326 327 334 335 336 336 337 337 338 ...
$ x : num 3.95 3.89 4.05 4.2 4.34 3.94 3.95 4.07 3.87 4 ...
$ y : num 3.98 3.84 4.07 4.23 4.35 3.96 3.98 4.11 3.78 4.05 ...
$ z : num 2.43 2.31 2.31 2.63 2.75 2.48 2.47 2.53 2.49 2.39 ...
head(diamonds)
Source: local data frame [6 x 10]
carat cut color clarity depth table price x y z
<dbl> <fctr> <fctr> <fctr> <dbl> <dbl> <int> <dbl> <dbl> <dbl>
1 0.23 Ideal E SI2 61.5 55 326 3.95 3.98 2.43
2 0.21 Premium E SI1 59.8 61 326 3.89 3.84 2.31
3 0.23 Good E VS1 56.9 65 327 4.05 4.07 2.31
4 0.29 Premium I VS2 62.4 58 334 4.20 4.23 2.63
5 0.31 Good J SI2 63.3 58 335 4.34 4.35 2.75
6 0.24 Very Good J VVS2 62.8 57 336 3.94 3.96 2.48
It is easy to plot the distribution of the diamonds prices with base graphics
hist(diamonds$price)

as well as with ggplot2
ggplot(diamonds, aes(x = price)) + geom_histogram()
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Now we subset the data to show the relationship between the diamonds weights (carat = 200 mg) and their prices ($):
set.seed(12345) # Make the sample reproducible
dsmall <- diamonds[sample(nrow(diamonds), 200), ]
Base graphics:
colorMap <- data.frame(color = rainbow(length(unique(dsmall$color))))
rownames(colorMap) <- unique(dsmall$color)
plot(price ~ carat, data = dsmall, col = colorMap[dsmall$color, "color"])
legend(x = 'bottomright',
legend = rownames(colorMap),
col = colorMap$color, pch = par("pch"), bty = 'n', xjust = 1)

ggplot2:
ggplot(data = dsmall, aes(x = carat, y = price, color = color)) + geom_point()

Geometric objects and Aesthetics
Geometic Objects (geom):
Geometric objects are the actual items we put on a plot. Examples include:
- points (geom_point, for scatter plots, dot plots, etc)
- lines (geom_line, for time series, trend lines, etc)
- boxplot (geom_boxplot, for, well, boxplots!)
A plot must have at least one geom; there is no upper limit. You can add a geom to a plot using the + operator.
You can get a list of available geometric objects using the code below:
help.search("geom_", package = "ggplot2")
or simply type geom_<tab> in any good R IDE (such as Rstudio or ESS) to see a list of functions starting with geom_.
Aesthetic Mapping
In ggplot an aesthetic mapping, defined with aes(), describes how variables are mapped to visual properties or aesthetics.
Examples of aesthetics are:
- position (i.e., on the x and y axes)
- color (“outside” color)
- fill (“inside” color)
- shape (of points)
- linetype
- size
Each type of geom accepts only a subset of all aesthetics. Refer to the geom help pages to see what mappings each geom accepts.
Scatter plots (geom_points)
p1 <- ggplot(dsmall, aes(x = carat, y = price))
p1 + geom_point()

p1 + geom_point(aes(color = color))

p1 + geom_point(aes(shape = cut))

p1 + geom_point(aes(shape = cut, color = color))

Aesthetic Mapping vs Assignment
Note that variables are mapped to aesthetics with the aes() function, while fixed aesthetics are set outside the aes() call.
This sometimes leads to confusion, as in this example:
ggplot(data = dsmall, aes(x = carat, y = price)) +
geom_point(aes(size = 2), # this is conceptually wrong since 2 is not a variable
color = "darkgreen") # this is ok since you might want all points to be green.

ggplot(data = dsmall, aes(x = carat, y = price)) +
geom_point(aes(fill = cut), size = 2, color = "black", shape = 25)

Available shape configurations
## A look at all 25 symbols
df2 <- data.frame(x = 1:5 , y = 1:25, z = 1:25)
s <- ggplot(df2, aes(x = x, y = y))
s + geom_point(aes(shape = z), size = 3, colour = "blue") +
scale_shape_identity()

## While all symbols have a foreground colour, symbols 19-25 also take a
## background colour (fill)
s + geom_point(aes(shape = z), size = 3, colour = "darkgreen", fill = "orange") +
scale_shape_identity()

Text labels
Use even a smaller subset to avoid cluttering:
set.seed(12345) # Make the sample reproducible
dsmall2 <- diamonds[sample(nrow(diamonds), 100), ]
p2 <- ggplot(dsmall2, aes(x = log(carat), y = log(price)))
p2 + geom_text(aes(label = color))

p2 + geom_label(aes(label = color))

The ggreplel gives an easy way to annotate the labels when they are densely packed.
library(ggrepel)
p2 + geom_point() + geom_text_repel(aes(label=color), size = 3)

It doesn’t work that well though if you have too many points clustered together, then the lines pointing to the points will extend too far way, to make room for all the labels.
p1 + geom_point() + geom_text_repel(aes(label=color), size = 3)

In these cases you should choose to label only a subset of points.
set.seed(123456)
subsetData <- subset(dsmall, sample(c(TRUE, FALSE), nrow(dsmall), replace = TRUE,
prob = c(0.2, 0.8)))
p1 + geom_point() +
geom_text_repel(data = subsetData, aes(label=color), size = 5, col = "Blue")

The Economist Data
For practice, you will try to recreate a plot published in the Economist issue of July 20th, 2016 reflecting the relationship between well-being and financial inclusion.
Graph source: http://www.economist.com/blogs/graphicdetail/2016/07/daily-chart-13
You will generate this figure step by step through a series of included exercises using the tools we’ve just learned and will learn about.
The data for the exercises is available in the dataSets/EconomistData.csv file. Read it in with the following commands:
dat <- read.csv("./data/EconomistData.csv")
head(dat)
Country SEDA.Current.level SEDA.Recent.progress Wealth.to.well.being.coefficient Growth.to.well.being.coefficient
1 Albania 50.0 63.3 1.27 1.31
2 Algeria 40.6 46.5 0.87 1.03
3 Angola 17.8 76.2 0.54 1.21
4 Argentina 54.1 49.1 0.91 0.89
5 Armenia 43.8 46.0 1.25 1.11
6 Australia 87.9 40.9 1.07 0.92
Percent.of.15plus.with.bank.account EPI_regions Region
1 37.98635 Central and Eastern Europ Europe
2 50.47579 Middle East and North Africa Middle East & North Africa
3 29.31812 Sub-Saharan Africa Sub-Saharan Africa
4 50.19730 Latin America and Caribbe Latin America & the Caribbean
5 17.66907 Middle East and North Africa Middle East & North Africa
6 98.85957 East Asia and the Pacific Oceania
The original sources for this data are:
- the Boston Consulting Group’s report on countries’ well-being, which includes Sustainable Economic Development Assessment (SEDA) scores, powerful diagnostics designed to provide government leaders with a perspective on how effectively their countries convert wealth, as measured by income levels, into well-being
- the World Bank Global Findex database, which records the indices of financial inclusion, including the percent of people aged 15 or more with a bank account.
The countries assignment to regions is based on the EPI_regions column in the countryExData data.frame from rworldmap package. The Region variable was matched with the categories in the Economist plot.
Exercise I
For the EconomistData.csv do the following:
- Create a scatter plot with percent of people over the age of 15 with a bank account on the x axis and the SEDA score on the y axis.
- Color the points in the previous plot blue.
- Color the points in the previous plot according to the
Region.
- Create boxplots of SEDA scores by
Region.
- Overlay points on top of the box plots
# (...?)
Statistical Transformations
Now, we will go back to the diamonds data set and return to the Economist plot later.
So far we have only dealt with the (x,y) type of plots (scatter plots or line plots) where each of the point has its corresponding (x,y) coordinate.
Sometimes, however, we are more interested in plots that require some statistical transformations. The transformations might map a raw datapoint or a group of datapoints to other values. Examples of plots involving statistical transformations:
- boxplots we just generated for the Economist data,
- histograms
- prediction lines etc.
- bar charts
These types of plots require some statistical transformations. For example:
- boxplots require computations of the the median, lower and upper quartile, and 1.5 * IQR of the y-values,
- smoothers compute the predicted values for y-values,
- histograms group the values into bins,
- bar charts counts the classes occurrences.
Boxplots and jittered points
ggplot(data = diamonds, aes(x = color, y =price/carat)) +
geom_jitter()

j1 <- ggplot(data = diamonds, aes(x = color, y =price/carat)) +
geom_jitter(alpha = I(1/5))
j2 <- ggplot(data = diamonds, aes(x = color, y =price/carat)) +
geom_jitter(alpha = I(1/50))
j3 <- ggplot(data = diamonds, aes(x = color, y =price/carat)) +
geom_jitter(alpha = I(1/200))
grid.arrange(j1, j2, j3, ncol = 3)

Here we used grid.arrange() for the package gridExtra to display multiple plots in the same line.
Sometimes less is more…
ggplot(data = diamonds, aes(x = color, y =price/carat)) +
geom_boxplot()

Histogram and density plots
Below we plot the distribution of the weights (carat) of the diamonds.
h <- ggplot(data = diamonds, aes(x = carat)) + geom_histogram()
d <- ggplot(data = diamonds, aes(x = carat)) + geom_density()
grid.arrange(h, d, ncol = 2)
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

- For the density plot, the
adjust argument controls the degree of smoothness (high values of adjust produce smoother plots).
- For the histogram, the
binwidth or bins argument controls the amount of smoothing by setting the bin size or the number of bins. (Break points can also be specified explicitly, using the breaks argument.)
p <- ggplot(data = diamonds, aes(x = carat)) + xlim(0, 3)
h1 <- p + geom_histogram(binwidth = 1)
h2 <- p + geom_histogram(binwidth = 0.1)
h3 <- p + geom_histogram(binwidth = 0.01)
grid.arrange(h1, h2, h3, ncol = 3)
Warning: Removed 32 rows containing non-finite values (stat_bin).
Warning: Removed 32 rows containing non-finite values (stat_bin).
Warning: Removed 32 rows containing non-finite values (stat_bin).

d1 <- p + geom_density(adjust = 5)
d2 <- p + geom_density(adjust = 1)
d3 <- p + geom_density(adjust = 1/5)
grid.arrange(d1, d2, d3, ncol = 3)
Warning: Removed 32 rows containing non-finite values (stat_density).
Warning: Removed 32 rows containing non-finite values (stat_density).
Warning: Removed 32 rows containing non-finite values (stat_density).

The histograms can be broken down into groups. Here we show grouping by diamonds cut.
h <- p + geom_histogram(aes(fill = cut), position = "dodge", bins = 10)
d <- p + geom_density(aes(color = cut))
grid.arrange(h, d, ncol = 2)
Warning: Removed 32 rows containing non-finite values (stat_bin).
Warning: Removed 4 rows containing missing values (geom_bar).
Warning: Removed 32 rows containing non-finite values (stat_density).

Instead of the marginal distribution, we can plot the components stacked on top of each other to see the contribution from each of group.
h <- p + geom_histogram(aes(fill = cut), position = "stack")
d <- p + geom_density(aes(fill = cut), position = "stack")
grid.arrange(h, d, ncol = 2)
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 32 rows containing non-finite values (stat_bin).
Warning: Removed 32 rows containing non-finite values (stat_density).

Bar charts
The discrete analogue of histogram is the bar chart, geom = "bar". Instead of partitioning the values into bins like histograms, the bar geom counts the number of instances of each discrete class. The counts are then plotted as columns for each distinct class.
If you’d like to tabulate class members in some other way (rather than count), e.g. by summing up a continuous variable, you can use the weight aesthetic.
b1 <- ggplot(diamonds, aes(x = color)) + geom_bar()
b2 <- ggplot(diamonds, aes(x = color)) + geom_bar(aes(weight = carat)) + ylab("carat")
grid.arrange(b1, b2, ncol = 2)

The left plot shows counts and the right plot is the count weighted by weight = carat to show the total weight of diamonds of each color.
Thus, you don’t need to tabulate your values beforehand, as with barchart in base R. However, if you did already summarize your data, you can still use geom_bar but using another statistical transformation stat = "identity rather than the default stat = "count".
diamonds.mean <- aggregate(diamonds["carat"], diamonds["color"], FUN=mean)
rbind(head(diamonds.mean), tail(diamonds.mean))
color carat
1 D 0.6577948
2 E 0.6578667
3 F 0.7365385
4 G 0.7711902
5 H 0.9117991
6 I 1.0269273
21 E 0.6578667
31 F 0.7365385
41 G 0.7711902
51 H 0.9117991
61 I 1.0269273
7 J 1.1621368
The default option will generate an error:
ggplot(diamonds.mean, aes(x=color, y=carat)) +
geom_bar()
Error: stat_count() must not be used with a y aesthetic.

Thus, you need to use the following:
ggplot(diamonds.mean, aes(x=color, y=carat)) +
geom_bar(stat="identity")

diamonds.sum <- aggregate(diamonds["carat"], diamonds["color"], FUN=sum)
ggplot(diamonds.sum, aes(x=color, y=carat)) + geom_bar(stat="identity")

Note that this is the same plot as the one generated with the weight aesthetic, which is exactly what we should expect.
Prediction lines
We can include a regression line to plot by simply adding the line with the fitted y-values from a prediction model:
dsmall$pred.price <- predict(lm(price ~ carat, data = dsmall))
p1 <- ggplot(dsmall, aes(x = carat, y = price))
p1 + geom_point(aes(color = color)) + geom_line(aes(y = pred.price))

Smoothers
If you have a scatterplot with many data points, it can be hard to see exactly what trend is shown by the data. In this case you may want to add a smoothed line to the plot. The smooth geom includes a line and a ribbon.
ggplot(data = diamonds, aes(x = carat, y = price)) +
geom_point() + geom_smooth()

For our small subset of the diamonds data set we have:
p1 + geom_point() + geom_smooth()

Changing the span argument, we can obtain more or less wiggly curve (smaller span results in more wiggliness).
grid.arrange(p1 + geom_point() + geom_smooth(span = 0.2),
p1 + geom_point() + geom_smooth(span = 0.7), ncol = 2)

- The default method used in
geom_smooth with small number of observations (n < 1000) is method = "loess" which uses a smooth local regression. More details about the algorithm used can be found in ?loess.
- Loess does not work well for large datasets (it’s \(O(n^2)\) in memory), and so an alternative smoothing algorithm is used when n is greater than 1,000.
Exercise II
- Re-create a scatter plot with percent of people aged 15+ with a bank account on the x axis and SEDA current level score on the y axis (as you did in the previous exercise).
- Overlay a smoothing line on top of the scatter plot using the lm method. Hint: see
?stat_smooth.
- Overlay a smoothing line on top of the scatter plot using the default method.
- Overlay a smoothing line on top of the scatter plot using the default loess method, but make it less smooth. Hint: see
?loess.
# (...?)
Scales
Aesthetic Mapping Variable Scaling
Aesthetic mapping (i.e., with aes()) is responsible for assigning an aesthetic to a variable. It doesn’t however specify how mapping should be done.
For example, aes(shape = x) or aes(color = z) do not specify what shapes or what colors should be used. To choose colors/shapes/sizes etc. you need to modify the corresponding scale.
In ggplot2 scales include:
- position
- color and fill
- size
- shape
- line type
Scales are modified with a series of functions using a scale_<aesthetic>_<type> naming scheme. Try typing scale_<tab> to see a list of scale modification functions.
Common Scale Arguments:
- name: the first argument gives the axis or legend title
- limits: the minimum and maximum of the scale
- breaks: the points along the scale where labels should appear
- labels: the labels that appear at each break
Scale: axes
Square root transformation of the y-axis:
p1 <- ggplot(dsmall, aes(x = carat, y = price))
p1 + geom_point() + scale_y_sqrt()

Log bas 10 transformation of the y-axis:
p1 + geom_point() + scale_y_log10()

Log base 10 transformation of x and y axes:
p1 + geom_point() + scale_y_log10() + scale_x_log10()

Note that the above produces the same points as:
ggplot(dsmall, aes(x = log(carat), y = log(price))) + geom_point()

but with different values on the axes.
Scale: shapes
p1 + geom_point(aes(shape = cut), size = 3)

p1 + geom_point(aes(shape = cut), size = 3) +
scale_shape_manual(values = c(1:5))

Scale: colors
To choose specific colors for discrete variables we can use scale_color_manual
p1 + geom_point(aes(color = cut), size = 3) +
scale_color_manual(values = c("red", "orange", "yellow", "green", "blue"))

For continuous variables you can also use scale_color_gradient, and specify the ends of the spectrum:
p1 + geom_point(aes(color = price), size = 3) +
scale_color_gradient(low = "blue", high = "red")

scale_color_brewer is a very useful function that can be used to set colors for discrete variables. It gives you a choice between many predefined and pretty color palettes.
p1 + geom_point(aes(color = cut), size = 3) +
scale_color_brewer(palette = "Set2")

Unfortunately scale_color_brewer doesn’t work for continuous variables:
p1 + geom_point(aes(shape = price), size = 3) +
scale_color_brewer(palette = "Spectral")
Error: A continuous variable can not be mapped to shape

Thankfully, we can get around this issue using the RColorBrewer package and using scale_color_gradientn:
library(RColorBrewer)
p1 + geom_point(aes(color = price), size = 3) +
scale_color_gradientn(colours = brewer.pal(name = "Spectral", n = 10))

… and if you are a true indie person, you can is even check out the color schemes based on Wes Anderson movies:
#install.packages("wesanderson")
library(wesanderson)
names(wes_palettes)
[1] "GrandBudapest" "Moonrise1" "Royal1" "Moonrise2" "Cavalcanti" "Royal2" "GrandBudapest2"
[8] "Moonrise3" "Chevalier" "Zissou" "FantasticFox" "Darjeeling" "Rushmore" "BottleRocket"
[15] "Darjeeling2"
For discrete:
p1 + geom_point(aes(color = cut), size = 3) +
scale_color_manual(values = wes_palette("Darjeeling", n = 5))

For continuous:
p1 + geom_point(aes(color = price), size = 3) +
scale_color_gradientn(colours = wes_palette("Darjeeling", 100, type = "continuous"))

You can also scale the values of the variable corresponding to color.
p1 + geom_point(aes(color = price), size = 3) +
scale_color_gradient(low = "blue", high = "red", trans = "log10")

and add your own breaks
p1 + geom_point(aes(color = price), size = 3) +
scale_color_gradient(low = "blue", high = "red", trans = "log10",
breaks = c(1000, 2000, 5000, 10000),
labels = c(" 1000", " 2000", " 5000", "10000"))

Exercise III
- For the scatter plot of % of ppl aged 15+ with bank account vs SEDA score colored by region, generated in Exercise I.3 modify the color scale to use specific values of your choosing. Hint: see
?scale_color_manual.
# (...?)
Faceting
p <- ggplot(diamonds, aes(x = carat))
p + geom_histogram()
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

p + geom_histogram() + facet_wrap(~ color)
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

p + geom_histogram() + facet_grid(cut ~ color)
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

(p <- ggplot(data = diamonds[sample(nrow(diamonds), 1000), ],
aes(x = carat, y = price)) +
geom_point(aes(text = paste("Clarity:", clarity)), size = 1) +
geom_smooth(aes(colour = cut, fill = cut)) + facet_wrap(~ cut))

Excercise IV
- Facet by region (
~ Region) the the Economist plot from Exercise III.
# (... ?)
Finish the Economist plot.
To complete the graph we need to:
- add a trend line
- change the axis labels
- change the order of the Region labels
- change the coloring of the points
- label selected points
- change color legend’s position
- adjust the axes ratio
- fix the tick marks
- match the plot’s theme with the Economist theme
- add notes
Change order of the Regions
dat$Region <- as.character(dat$Region)
dat$Region <- factor(dat$Region,
levels = c("Europe", "Asia", "Oceania",
"North America",
"Latin America & the Caribbean",
"Middle East & North Africa",
"Sub-Saharan Africa"),
labels = c("Europe", "Asia", "Oceania",
"North America",
"Latin America & \n the Caribbean",
"Middle East & \n North Africa",
"Sub-Saharan \n Africa"))
ggplot(dat, aes(Percent.of.15plus.with.bank.account, SEDA.Current.level)) +
geom_point(aes(color = Region))

Add the linear trend
# (...?)
Change the axes ratio.
Hint ?coord_fixed
# (...?)
Change the color scheme
Use the following colors c("#28AADC","#F2583F", "#76C0C1","#24576D", "#248E84","#DCC3AA", "#96503F")
# (...?)
Change the background and theme
You can check out the ggthemes package which implement the themes that make your plots look like they came from:
- Base graphics
- Tableau
- Excel
- Stata
- the Economist
- Wall Street Journal
- Edward Tufte
- Nate Silver’s Fivethirtyeight
- etc.
use the one than mimics the Economist.
library(ggthemes)
# (...?)
Add point labels
Add labels to the following subset of the countries:
pointsToLabel <- c("Yemen", "Iraq", "Egypt", "Jordan", "Chad", "Congo",
"Angola", "Albania", "Zimbabwe", "Uganda", "Nigeria",
"Uruguay", "Kazakhstan", "India", "Turkey", "South Africa",
"Kenya", "Russia", "Brazil", "Chile", "Saudi Arabia",
"Poland", "China", "Serbia", "United States", "United Kingdom")
Use geom_text_repel()
# (...?)
Add notes to the bottom and save the plot
Use grid.text()
# (...?)
plotly for interactive plotting
You can also easily generate an interactive plot by calling ggplotly() function from plotly package.
#install.packages("plotly")
library(plotly)
#ggplotly(pEconomist)
Similar to the original:
What we have learned so far:
- 2D plotting in R can be done with:
- The building blocks of
ggplot2
- How to generate:
- line plots
- scatter plots
- histograms
- bar plots
- boxplots
- prediction/trend lines or smoothers
- How to modify the aesthetics settings:
- How to use themes to automatically change the style of a plot.
- Facet the plot to display the information for different subsets of data with different values of a specific attribute.
LS0tCnRpdGxlOiAiR2VuZXJhdGluZyBncmFwaGljcyB3aXRoIFIiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMgT2JqZWN0aXZlcwoKSW4gdGhpcyBzZWN0aW9uIHdlIHdpbGwgbGVhcm4gaG93IHRvIGdlbmVyYXRlIGdyYXBoaWNzIGluIFIuCgpUaGUgbWF0ZXJpYWwgcHJlc2VudGVkIGlzIGhlcmUgbGFyZ2VseSBiYXNlZCBvbiB0aGUgbWF0ZXJpYWwgcHJlc2VudGVkIGluOgoKKiBbd29ya3Nob3Agbm90ZXNdKGh0dHA6Ly90dXRvcmlhbHMuaXEuaGFydmFyZC5lZHUvUi9SZ3JhcGhpY3MvUmdyYXBoaWNzLmh0bWwpIGJ5IERhdGEgU2NpZW5jZSBTZXJ2aWNlcyBhdCBIYXJ2YXJkIElRU1MgCiogYW5kIFtDaGFwdGVyIDJdKGh0dHA6Ly9nZ3Bsb3QyLm9yZy9ib29rL3FwbG90LnBkZikgZnJvbSBIYWRsZXkgV2lja2hhbSdzIGJvb2sgb24gZ2dwbG90Mi4KCiMgSW5zdGFsbCBhbmQgbG9hZCBwYWNrYWdlcwoKYGBge3IgY2hlY2staW5zdGFsbC1sb2FkLXBhY2thZ2VzLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojIENsZWFyIHRoZSB3b3Jrc3BhY2UKcm0obGlzdCA9IGxzKCkpCgojIExpc3Qgb2YgcGFja2FnZSBuZWVkZWQgZm9yIHRoaXMgd29ya3Nob3AKcmVxcGtnIDwtIGMoImdncGxvdDIiLCAiZ2dyZXBlbCIsICJnZ3RoZW1lcyIsICJncmlkIiwgImdyaWRFeHRyYSIsIAogICAgICAgICAgICAiUkNvbG9yQnJld2VyIikKCiMgQ2hlY2sgaWYgdGhlIHBhY2thZ2VzIGFyZSBpbnN0YWxsZWQ6CmlucGtnID0gaW5zdGFsbGVkLnBhY2thZ2VzKClbLCAiUGFja2FnZSJdICNpbnN0YWxsZWQgcGFja2FnZXMKbmVlZGVkcGtnID0gcmVxcGtnWyFyZXFwa2cgJWluJSBpbnBrZ10KaWYobGVuZ3RoKG5lZWRlZHBrZykgPiAwKXsKICBzdG9wKHBhc3RlKCJcbiBOZWVkIHRvIGluc3RhbGwgdGhlIGZvbGxvd2luZyBwYWNrYWdlOiIsIG5lZWRlZHBrZykpCn0KCiMgTG9hZCBhbGwgcmVxdWlyZWQgcGFja2FnZXMgYW5kIHNob3cgdmVyc2lvbgpmb3IoaSBpbiByZXFwa2cpewoJcHJpbnQocGFzdGUoaSwgInZlcnNpb246IiwgcGFja2FnZVZlcnNpb24oaSkpKQoJbGlicmFyeShpLCBxdWlldGx5PVRSVUUsIHZlcmJvc2U9RkFMU0UsIHdhcm4uY29uZmxpY3RzPUZBTFNFLCBjaGFyYWN0ZXIub25seT1UUlVFKQp9CmBgYAoKSWYgeW91IGhhdmVuJ3QgZG9uZSB0aGF0IGFscmVhZHksIGRvd25sb2FkIHRoZSB3b3Jrc2hvcCBtYXRlcmlhbHM6CmBgYHtyIGV2YWwgPSBGQUxTRX0Kc2V0d2QoIi9wYXRoL3RvL2Rpci8iKQpkb3dubG9hZC5maWxlKCIvdXJsL3RvL3dvcmtzaG9wL21hdGVyaWFscyIsICJSX3dvcmtzaG9wLnppcCIpCnVuemlwKCJSX3dvcmtzaG9wLnppcCIpCmBgYAoKCiMgSW50cm9kdWN0aW9uCgpJbnN0cnVjdGlvbnM6CgoqIEFzayAqYW55KiBxdWVzdGlvbiAqYW55KiB0aW1lIGlmICphbnkqdGhpbmcgaXMgdW5jbGVhciEKKiBDb2xsYWJvcmF0aW9uIGlzIGVuY291cmFnZWQuIEFzayBhIGZyaWVuZCEgVGhleSBtaWdodCBkZWFsdCB3aXRoIHRoZSBzYW1lCmlzc3VlIGp1c3QgYSBtaW51dGUgYWdvLgoqIEVhY2ggb2YgeW91IHNob3VsZCBoYXZlIDIgcG9zdC1pdHMuIFVzZSB0aGVtIHRvIGF0dGFjaCB0byB5b3UgbGFwdG9wLgogICAgKyA8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj5yZWQ8L3NwYW4+IC0tIGlmIHlvdSBjYW4ndCBmaWd1cmUgb3V0IGEgdGFzawogICAgKyA8c3BhbiBzdHlsZT0iY29sb3I6Z3JlZW4iPmdyZWVuPC9zcGFuPiAtLSBldmVyeXRoaW5nIGlzIGdvb2QKCgpUaGlzIGNvdXJzZSBhc3N1bWVzIHRoYXQgeW91OgoKKiBoYXZlIHNvbWUgZXhwZXJpZW5jZSB3aXRoIFIgYWxyZWFkeSAoZS5nLiB0aGUgcHJldmlvdXMgMiB3b3Jrc2hvcCBzZWN0aW9ucykKKiB3b3VsZCBsaWtlIHRvIGxlYXJuIGhvdyB0byBjcmVhdGUgY29vbCB2aXN1YWxpemF0aW9uIFIgd2l0aG91dCAKZ29pbmcgbXVjaCBpbnRvIGRldGFpbCBvZiB3aGF0IGNvbXB1dGF0aW9ucyBhcmUgZG9uZSBvbiB0aGUgc2lkZS4KCgojIFdoYXQgaXMgYGdncGxvdDJgPwoKPiBgZ2dwbG90MmAgaXMgYSBwbG90dGluZyBzeXN0ZW0gZm9yIFIsIGJhc2VkIG9uIHRoZSBncmFtbWFyIG9mIGdyYXBoaWNzLiBJdCB0YWtlcyBjYXJlIG9mIG1hbnkgb2YgdGhlIGZpZGRseSBkZXRhaWxzIHRoYXQgbWFrZSBwbG90dGluZyBhIGhhc3NsZSAobGlrZSBkcmF3aW5nIGxlZ2VuZHMpIGFzIHdlbGwgYXMgcHJvdmlkaW5nIGEgcG93ZXJmdWwgbW9kZWwgb2YgZ3JhcGhpY3MgdGhhdCBtYWtlcyBpdCBlYXN5IHRvIHByb2R1Y2UgY29tcGxleCBtdWx0aS1sYXllcmVkIGdyYXBoaWNzLiBeW2h0dHA6Ly9nZ3Bsb3QyLm9yZy9dCgpBZHZhbnRhZ2VzIG9mIGBnZ3Bsb3QyYDoKCiogcGxvdHMgYXJlIGRlZmluZWQgYXQgYSBoaWdoIGxldmVsIG9mIGFic3RyYWN0aW9uLAoqIHBsb3RzIGFyZSBicm9rZW4gZG93biBpbnRvIG1vZHVsZXMvbGF5ZXJzCiogYSBncmVhdCBmbGV4aWJpbGl0eSB3aGVuICpjdXN0b21pemluZyogeW91ciBwbG90CiogZ29vZCBkb2N1bWVudGF0aW9uIAoqIGEgbGFyZ2UgdXNlciBiYXNlIChlYXN5IGFjY2VzcyB0byBoZWxwKQoKV2Vha25lc3NlcyBvZiBgZ2dwbG90MmAgKHdoYXQgdGhlIHBhY2thZ2Ugc2hvdWxkIG5vdCBiZSB1c2VkIGZvcik6CgoqIDNEIGdyYXBoaWNzOiBzZWUgW2ByZ2xgXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvcmdsL3ZpZ25ldHRlcy9yZ2wuaHRtbCkgCnBhY2thZ2UgaW5zdGVhZCBvciBbYGdncGxvdDJgICsgYHBsb3RseWBdKGh0dHA6Ly9ibG9nLnJldm9sdXRpb25hbmFseXRpY3MuY29tLzIwMTQvMTEvMy1kLXBsb3RzLXdpdGgtcGxvdGx5Lmh0bWwpCiogZ3JhcGgvbmV0d29yayBwbG90cyB3aXRoIG5vZGVzIGFuZCBlZGdlczogc2VlIFtgaWdyYXBoYF0oaHR0cDovL2lncmFwaC5vcmcvci8pIHBhY2thZ2UgaW5zdGVhZAoqIGludGVyYWN0aXZlIGdyYXBoaWNzOiBzZWUgW2BnZ3Zpc2BdKGh0dHA6Ly9nZ3Zpcy5yc3R1ZGlvLmNvbS9nZ3Zpcy1iYXNpY3MuaHRtbCksIApbYHBsb3RseWBdKGh0dHBzOi8vZ2l0aHViLmNvbS9yb3BlbnNjaS9wbG90bHkpIHBhY2thZ2VzIGluc3RlYWQKCgojIFdoYXQgaXMgdGhlIGdyYW1tYXIgb2YgZ3JhcGhpY3M/CgpJdCBpcyBhIGNvbmNlcHQgY29pbmVkIGJ5IExlbGFuZCBXaWxraW5zb24gaW4gKjIwMDUqLgoKKipUaGUgYmFzaWMgaWRlYToqKiBhIHBsb3QgaXMgZGVmaW5lZCBieSBpbmRlcGVuZGVudCBidWlsZGluZyBibG9ja3MsIHdoaWNoCmNvbWJpbmVkIGNyZWF0ZSBqdXN0IGFib3V0IGFueSBraW5kIG9mIHZpc3VhbGl6YXRpb24geW91IHdhbnQuIAoKVGhlIGJ1aWxkaW5nIGJsb2NrcyBvZiBhIGdyYXBoIGluY2x1ZGU6CgoqIGRhdGEKKiBhZXN0aGV0aWMgbWFwcGluZwoqIGdlb21ldHJpYyBvYmplY3RzCiogc3RhdGlzdGljYWwgdHJhbnNmb3JtYXRpb25zCiogc2NhbGVzCiogY29vcmRpbmF0ZSBzeXN0ZW0KKiBwb3NpdGlvbmluZyBhZGp1c3RtZW50cwoqIGZhY3RlaW5nCgojIFRoZSBzdHJ1Y3R1cmUgb2YgZ2dwbG90IG9iamVjdAoKVGhlIGBnZ3Bsb3QoKWAgZnVuY3Rpb24gaXMgdXNlZCB0byBpbml0aWFsaXplIHRoZSBiYXNpYyBncmFwaCBzdHJ1Y3R1cmUuCkl0IGNhbm5vdCBwcm9kdWNlIHRoZSBwbG90IHdlIHdhbnQgYnkgaXRzZWxmLiBJbnN0ZWFkLCB3ZSBuZWVkIHRvIGFkZCBleHRyYSAKYnVpbGRpbmcgYmxvY2tzIGl0LiBUaGUgc3RydWN0dXJlIG9mIGEgZ2dwbG90IGxvb2tzIGxpa2UgdGhpczoKCmBgYHtyLCBldmFsID0gRkFMU0V9CmdncGxvdChkYXRhID0gPGRlZmF1bHQgZGF0YSBzZXQ+LCAKICAgICAgIGFlcyh4ID0gPGRlZmF1bHQgeCBheGlzIHZhcmlhYmxlPiwKICAgICAgICAgICB5ID0gPGRlZmF1bHQgeSBheGlzIHZhcmlhYmxlPiwKICAgICAgICAgICAuLi4gPG90aGVyIGRlZmF1bHQgYWVzdGhldGljIG1hcHBpbmdzPiksCiAgICAgICAuLi4gPG90aGVyIHBsb3QgZGVmYXVsdHM+KSArCiAgCiAgZ2VvbV88Z2VvbSB0eXBlPihhZXMoc2l6ZSA9IDxzaXplIHZhcmlhYmxlIGZvciB0aGlzIGdlb20+LCAKICAgICAgICAgICAgICAgICAgICAgICAuLi4gPG90aGVyIGFlc3RoZXRpYyBtYXBwaW5ncz4pLAogICAgICAgICAgICAgICAgICAgZGF0YSA9IDxkYXRhIGZvciB0aGlzIHBvaW50IGdlb20+LAogICAgICAgICAgICAgICAgICAgc3RhdCA9IDxzdGF0aXN0aWMgc3RyaW5nIG9yIGZ1bmN0aW9uPiwKICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gPHBvc2l0aW9uIHN0cmluZyBvciBmdW5jdGlvbj4sCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IDwiZml4ZWQgY29sb3Igc3BlY2lmaWNhdGlvbiI+LAogICAgICAgICAgICAgICAgICAuLi4gPG90aGVyIGFyZ3VtZW50cywgcG9zc2libHkgcGFzc2VkIHRvIHRoZSBfc3RhdF8gZnVuY3Rpb24pICsKCiAgc2NhbGVfPGFlc3RoZXRpYz5fPHR5cGU+KG5hbWUgPSA8InNjYWxlIGxhYmVsIj4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IDx3aGVyZSB0byBwdXQgdGljayBtYXJrcz4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IDxsYWJlbHMgZm9yIHRpY2sgbWFya3M+LAogICAgICAgICAgICAgICAgICAgICAgICAgICAuLi4gPG90aGVyIG9wdGlvbnMgZm9yIHRoZSBzY2FsZT4pICsKCiAgdGhlbWUocGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiZ3JheSIpLAogICAgICAgIC4uLiA8b3RoZXIgdGhlbWUgZWxlbWVudHM+KQpgYGAKClRoaXMgY2h1bmsgb2YgY29kZSBtaWdodCBzZWVtIGNvbmZ1c2luZywgYnV0IGJ5IHRoZSBlbmQgb2YgdGhpcyB3b3Jrc2hvcAp5b3Ugc2hvdWxkIGJlIGFibGUgdG8gdW5kZXJzdGFuZCBlYWNoIG9mIHRoZSBjb21wb25lbnRzLgoKKipUaGUgYmFzaWMgaWRlYSoqIGlzIHRoYXQgeW91ICpzcGVjaWZ5IGRpZmZlcmVudCBwYXJ0cyogb2YgdGhlIHBsb3QsIAphbmQgKmFkZCB0aGVtIHRvZ2V0aGVyKiB1c2luZyB0aGUgKyBvcGVyYXRvci4KCgojIGBnZ3Bsb3QyYCB2cyBiYXNlIGdyYXBoaWNzOgoKYGdncGxvdDJgIGNvbXBhcmVkIHRvIGJhc2UgZ3JhcGhpY3MgaXM6CgoqIG1vcmUgdmVyYm9zZSBmb3Igc2ltcGxlIC8gKm91dCBvZiB0aGUgYm94KiBncmFwaGljcwoqIGxlc3MgdmVyYm9zZSBmb3IgY29tcGxleCAvIGN1c3RvbSBncmFwaGljcwoqIHVzZXMgYSBkaWZmZXJlbnQgc3lzdGVtIGZvciBhZGRpbmcgcGxvdCBlbGVtZW50cwooYCtgIGFkZGluZyBvcGVyYXRvciBpbnN0ZWFkIG9mIGNhbGxpbmcgbmV3IGZ1bmN0aW9ucyBsaWtlIApwb2ludHMoKSwgbGluZXMoKSBldGMuKQoKIyMgRXhhbXBsZSAxOiBgSGlzdG9yeSBvZiB1bmVtcGxveWVtbnRgCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTZ9CmRhdGEoImVjb25vbWljcyIpCnN0cihlY29ub21pY3MpCmBgYAoKYGBge3J9CmhlYWQoZWNvbm9taWNzKQpgYGAKCkJhc2UgZ3JhcGhpY3Mgd2l0aCBhIHNpbXBsZSBgcGxvdCgpYCBmdW5jdGlvbjoKCmBgYHtyfQpwbG90KHVuZW1wbG95L3BvcCB+IGRhdGUsIGRhdGEgPSBlY29ub21pY3MsICB0eXBlID0gImwiKQpgYGAKCkEgc2ltaWxhciBwbG90IHVzaW5nIGBnZ3Bsb3QyYAoKYGBge3J9CmdncGxvdChkYXRhID0gZWNvbm9taWNzLCBhZXMoeCA9IGRhdGUsIHkgPSB1bmVtcGxveS9wb3ApKSArIGdlb21fbGluZSgpCmBgYAoKTm90ZSB0aGF0LCBgZ2dwbG90KClgIGJ5IGl0c2VsZiBkb2VzIG5vdCBwbG90IHRoZSBkYXRhLiAKYGBge3J9CmdncGxvdChkYXRhID0gZWNvbm9taWNzLCBhZXMoeCA9IGRhdGUsIHkgPSB1bmVtcGxveS9wb3ApKQpgYGAKWW91IG5lZWQgdG8gYWRkIHRoZSAqbGluZXMqIG9iamVjdC4KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGVjb25vbWljcywgYWVzKHggPSBkYXRlLCB5ID0gdW5lbXBsb3kvcG9wKSkgKyBnZW9tX2xpbmUoKQpgYGAKCi4uLiBhbmQgcG9zc2libHkgY2hhbmdlIHRoZSBiYWNrZ3JvdW5kIGNvbG9yIGZyb20gCjxzcGFuIHN0eWxlPSJjb2xvcjpncmF5Ij5kZWZhdWx0IGdyYXk8L3NwYW4+IHRvIAo8c3BhbiBzdHlsZT0iY29sb3I6IHdoaXRlOyBiYWNrZ3JvdW5kOmdyYXkiPmN1c3RvbWl6ZWQgd2hpdGU8L3NwYW4+LiAKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGVjb25vbWljcywgYWVzKHggPSBkYXRlLCB5ID0gdW5lbXBsb3kvcG9wKSkgKyBnZW9tX2xpbmUoKSArCiAgdGhlbWVfYncoKQpgYGAKCiMjIFdoYXQgaWYgd2Ugd2FudCB0byBjb21wYXJlIHRoZSB0cmVuZCBmcm9tIHllYXIgMjAwOSB0byAyMDE0PwoKQWRkIHR3byB2YXJpYWJsZXMgb25lIGZvciB5ZWFyIGFuZCBvbmUgZm9yIGRheSBvZiB0aGUgeWVhcjogCmBgYHtyfQplY29ub21pY3MkZGF5T2Z0aGVZZWFyIDwtIGZvcm1hdChlY29ub21pY3MkZGF0ZSwgZm9ybWF0PSIlbS0lZCIpCmVjb25vbWljcyRkYXlPZnRoZVllYXIgPC0gYXMuRGF0ZShlY29ub21pY3MkZGF5T2Z0aGVZZWFyLCBmb3JtYXQ9IiVtLSVkIikKZWNvbm9taWNzJHllYXIgPC0gZm9ybWF0KGVjb25vbWljcyRkYXRlLCBmb3JtYXQ9IiVZIikKaGVhZChlY29ub21pY3MpCmBgYAoKVXNpbmcgYmFzZSBncmFwaGljczoKCmBgYHtyfQpwbG90KHVuZW1wbG95L3BvcCB+IGRheU9mdGhlWWVhciwgZGF0YSA9IHN1YnNldChlY29ub21pY3MsIHllYXIgPT0gMjAwOSksIAogICAgIHlsaW0gPSBjKDAuMDI1LCAwLjA1KSwgdHlwZSA9ICJsIikKbGluZXModW5lbXBsb3kvcG9wIH4gZGF5T2Z0aGVZZWFyLCBjb2wgPSAicmVkIiwgZGF0YSA9IHN1YnNldChlY29ub21pY3MsIHllYXIgPT0gMjAxNCkpCmxlZ2VuZCgidG9wbGVmdCIsCiAgICAgICBjKCIyMDA4IiwgIjIwMTQiKSwgdGl0bGU9IlllYXIiLAogICAgICAgY29sPWMoImJsYWNrIiwgInJlZCIpLAogICAgICAgcGNoPWMoMSwgMSkpCmBgYAoKVXNpbmcgYGdncGxvdDJgOgoKYGBge3J9CmdncGxvdChkYXRhID0gc3Vic2V0KGVjb25vbWljcywgeWVhciAlaW4lIGMoMjAxNCwgMjAwOSkpLCAKICAgICAgIGFlcyh4ID0gZGF5T2Z0aGVZZWFyLCB5ID0gdW5lbXBsb3kvcG9wKSkgKyAKICBnZW9tX2xpbmUoYWVzKGNvbG9yID0geWVhcikpIApgYGAKCk5vdGUgdGhhdCB0aGVyZSBpcyBubyBuZWVkIHRvIHNwZWNpZnkgdGhlIGxlZ2VuZCEgSXQgaXMgcHJvZHVjZWQgYXV0b21hdGljYWxseQppbiBgZ2dwbG90MmAuCgpJdCBpcyBlYXN5IHRvIGV2ZW4gcGxvdCBhbGwgdGhlIHllYXJzIHRvZ2V0aGVyOgoKYGBge3IgZmlnLmhlaWdodD01fQpnZ3Bsb3QoZGF0YSA9IGVjb25vbWljcywgYWVzKHggPSBkYXlPZnRoZVllYXIsIHkgPSB1bmVtcGxveS9wb3ApKSArIAogIGdlb21fbGluZShhZXMoY29sb3IgPSB5ZWFyKSkKYGBgCgojIyBFeGFtcGxlIDI6IGRpYW1vbmRzIGRhdGFzZXQKCldlIHdpbGwgbm93IGxvYWQgYSBgZGlhbW9uZHNgIGRhdGEgc2V0IHRoYXQgaXMgaW5jbHVkZWQgaW4gd2l0aCB0aGUgYGdncGxvdDJgIApwYWNrYWdlLiAKClRoZSBkYXRhIHNldCBjb250YWlucyB0aGUgcHJpY2VzIGFuZCBvdGhlciBhdHRyaWJ1dGVzIG9mIGFsbW9zdCA1NCwwMDAgCmRpYW1vbmRzLiBZb3UgY2FuIGNhbGwgYD9kaWFtb25kc2AgdG8gbGVhcm4gbW9yZSBhYm91dCB0aGUgYXZhaWxhYmxlIGF0dHJpYnV0ZXMuCgpgYGB7cn0KZGF0YSgiZGlhbW9uZHMiKQpzdHIoZGlhbW9uZHMpCmBgYAoKYGBge3J9CmhlYWQoZGlhbW9uZHMpCmBgYAoKSXQgaXMgZWFzeSB0byBwbG90IHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGRpYW1vbmRzIHByaWNlcyB3aXRoIGJhc2UgZ3JhcGhpY3MKCmBgYHtyfQpoaXN0KGRpYW1vbmRzJHByaWNlKQpgYGAKCmFzIHdlbGwgYXMgd2l0aCBgZ2dwbG90MmAKCmBgYHtyfQpnZ3Bsb3QoZGlhbW9uZHMsIGFlcyh4ID0gcHJpY2UpKSArIGdlb21faGlzdG9ncmFtKCkKYGBgCgpOb3cgd2Ugc3Vic2V0IHRoZSBkYXRhIHRvIHNob3cgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBkaWFtb25kcwp3ZWlnaHRzIChjYXJhdCA9IDIwMCBtZykgYW5kIHRoZWlyIHByaWNlcyAoJCk6IAoKYGBge3J9CnNldC5zZWVkKDEyMzQ1KSAjIE1ha2UgdGhlIHNhbXBsZSByZXByb2R1Y2libGUKZHNtYWxsIDwtIGRpYW1vbmRzW3NhbXBsZShucm93KGRpYW1vbmRzKSwgMjAwKSwgXQpgYGAKCkJhc2UgZ3JhcGhpY3M6CgpgYGB7cn0KY29sb3JNYXAgPC0gZGF0YS5mcmFtZShjb2xvciA9IHJhaW5ib3cobGVuZ3RoKHVuaXF1ZShkc21hbGwkY29sb3IpKSkpCnJvd25hbWVzKGNvbG9yTWFwKSA8LSB1bmlxdWUoZHNtYWxsJGNvbG9yKQoKcGxvdChwcmljZSB+IGNhcmF0LCBkYXRhID0gZHNtYWxsLCBjb2wgPSBjb2xvck1hcFtkc21hbGwkY29sb3IsICJjb2xvciJdKQpsZWdlbmQoeCA9ICdib3R0b21yaWdodCcsIAogICAgICAgbGVnZW5kID0gcm93bmFtZXMoY29sb3JNYXApLAogICAgICAgY29sID0gY29sb3JNYXAkY29sb3IsIHBjaCA9IHBhcigicGNoIiksIGJ0eSA9ICduJywgeGp1c3QgPSAxKQpgYGAKCmBnZ3Bsb3QyYDoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRzbWFsbCwgYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlLCBjb2xvciA9IGNvbG9yKSkgKyBnZW9tX3BvaW50KCkKYGBgCgoKIyBHZW9tZXRyaWMgb2JqZWN0cyBhbmQgQWVzdGhldGljcwoKIyMgR2VvbWV0aWMgT2JqZWN0cyAoYGdlb21gKToKCkdlb21ldHJpYyBvYmplY3RzIGFyZSB0aGUgYWN0dWFsIGl0ZW1zIHdlIHB1dCBvbiBhIHBsb3QuIEV4YW1wbGVzIGluY2x1ZGU6CgoqIHBvaW50cyAoZ2VvbV9wb2ludCwgZm9yIHNjYXR0ZXIgcGxvdHMsIGRvdCBwbG90cywgZXRjKQoqIGxpbmVzIChnZW9tX2xpbmUsIGZvciB0aW1lIHNlcmllcywgdHJlbmQgbGluZXMsIGV0YykKKiBib3hwbG90IChnZW9tX2JveHBsb3QsIGZvciwgd2VsbCwgYm94cGxvdHMhKQoKQSBwbG90IG11c3QgaGF2ZSBhdCBsZWFzdCBvbmUgZ2VvbTsgdGhlcmUgaXMgbm8gdXBwZXIgbGltaXQuIApZb3UgY2FuIGFkZCBhIGdlb20gdG8gYSBwbG90IHVzaW5nIHRoZSBgK2Agb3BlcmF0b3IuCgpZb3UgY2FuIGdldCBhIGxpc3Qgb2YgYXZhaWxhYmxlIGdlb21ldHJpYyBvYmplY3RzIHVzaW5nIHRoZSBjb2RlIGJlbG93OgoKYGBge3J9CmhlbHAuc2VhcmNoKCJnZW9tXyIsIHBhY2thZ2UgPSAiZ2dwbG90MiIpCmBgYAoKb3Igc2ltcGx5IHR5cGUgYGdlb21fPHRhYj5gIGluIGFueSBnb29kIFIgSURFIChzdWNoIGFzIFJzdHVkaW8gb3IgRVNTKSB0byBzZWUKYSBsaXN0IG9mIGZ1bmN0aW9ucyBzdGFydGluZyB3aXRoIGBnZW9tX2AuCgoKIyMgQWVzdGhldGljIE1hcHBpbmcKCj4gSW4gZ2dwbG90IGFuICphZXN0aGV0aWMgbWFwcGluZyosIGRlZmluZWQgd2l0aCBhZXMoKSwgZGVzY3JpYmVzIGhvdyB2YXJpYWJsZXMgCmFyZSBtYXBwZWQgdG8gdmlzdWFsIHByb3BlcnRpZXMgb3IgYWVzdGhldGljcy4KCkV4YW1wbGVzIG9mIGFlc3RoZXRpY3MgYXJlOiAKCiogcG9zaXRpb24gKGkuZS4sIG9uIHRoZSB4IGFuZCB5IGF4ZXMpCiogY29sb3IgKCJvdXRzaWRlIiBjb2xvcikKKiBmaWxsICgiaW5zaWRlIiBjb2xvcikKKiBzaGFwZSAob2YgcG9pbnRzKQoqIGxpbmV0eXBlCiogc2l6ZQoKRWFjaCB0eXBlIG9mIGdlb20gYWNjZXB0cyBvbmx5IGEgc3Vic2V0IG9mIGFsbCBhZXN0aGV0aWNzLiBSZWZlciB0bwp0aGUgZ2VvbSBoZWxwIHBhZ2VzIHRvIHNlZSB3aGF0IG1hcHBpbmdzIGVhY2ggZ2VvbSBhY2NlcHRzLiAKCiMjIFNjYXR0ZXIgcGxvdHMgKGBnZW9tX3BvaW50c2ApCgoKYGBge3J9CnAxIDwtIGdncGxvdChkc21hbGwsIGFlcyh4ID0gY2FyYXQsIHkgPSBwcmljZSkpCnAxICsgZ2VvbV9wb2ludCgpCmBgYAoKYGBge3J9CnAxICsgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBjb2xvcikpCmBgYAoKYGBge3J9CnAxICsgZ2VvbV9wb2ludChhZXMoc2hhcGUgPSBjdXQpKQpgYGAKCmBgYHtyfQpwMSArIGdlb21fcG9pbnQoYWVzKHNoYXBlID0gY3V0LCBjb2xvciA9IGNvbG9yKSkKYGBgCgojIyBBZXN0aGV0aWMgTWFwcGluZyB2cyBBc3NpZ25tZW50CgpOb3RlIHRoYXQgdmFyaWFibGVzIGFyZSBtYXBwZWQgdG8gYWVzdGhldGljcyB3aXRoIHRoZSBgYWVzKClgIGZ1bmN0aW9uLCAKd2hpbGUgZml4ZWQgYWVzdGhldGljcyBhcmUgc2V0IG91dHNpZGUgdGhlIGBhZXMoKWAgY2FsbC4gCgpUaGlzIHNvbWV0aW1lcyBsZWFkcyB0byBjb25mdXNpb24sIGFzIGluIHRoaXMgZXhhbXBsZToKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRzbWFsbCwgYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlKSkgKyAKICBnZW9tX3BvaW50KGFlcyhzaXplID0gMiksICMgdGhpcyBpcyBjb25jZXB0dWFsbHkgd3Jvbmcgc2luY2UgMiBpcyBub3QgYSB2YXJpYWJsZQogICAgICAgICAgICAgY29sb3IgPSAiZGFya2dyZWVuIikgIyB0aGlzIGlzIG9rIHNpbmNlIHlvdSBtaWdodCB3YW50IGFsbCBwb2ludHMgdG8gYmUgZ3JlZW4uCmBgYAoKYGBge3J9CmdncGxvdChkYXRhID0gZHNtYWxsLCBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKSArIAogIGdlb21fcG9pbnQoYWVzKGZpbGwgPSBjdXQpLCBzaXplID0gMiwgY29sb3IgPSAiYmxhY2siLCBzaGFwZSA9IDI1KQpgYGAKCgojIyBBdmFpbGFibGUgc2hhcGUgY29uZmlndXJhdGlvbnMKCmBgYHtyfQojIyBBIGxvb2sgYXQgYWxsIDI1IHN5bWJvbHMKZGYyIDwtIGRhdGEuZnJhbWUoeCA9IDE6NSAsIHkgPSAxOjI1LCB6ID0gMToyNSkKcyA8LSBnZ3Bsb3QoZGYyLCBhZXMoeCA9IHgsIHkgPSB5KSkKcyArIGdlb21fcG9pbnQoYWVzKHNoYXBlID0geiksIHNpemUgPSAzLCBjb2xvdXIgPSAiYmx1ZSIpICsKICBzY2FsZV9zaGFwZV9pZGVudGl0eSgpCmBgYAoKCmBgYHtyfQojIyBXaGlsZSBhbGwgc3ltYm9scyBoYXZlIGEgZm9yZWdyb3VuZCBjb2xvdXIsIHN5bWJvbHMgMTktMjUgYWxzbyB0YWtlIGEKIyMgYmFja2dyb3VuZCBjb2xvdXIgKGZpbGwpCnMgKyBnZW9tX3BvaW50KGFlcyhzaGFwZSA9IHopLCBzaXplID0gMywgY29sb3VyID0gImRhcmtncmVlbiIsIGZpbGwgPSAib3JhbmdlIikgKwogIHNjYWxlX3NoYXBlX2lkZW50aXR5KCkKYGBgCgoKIyMgRGF0YSB0cmFuc2Zvcm1hdGlvbnMKCldlIGNhbiBwbG90IHRoZSB0cmFuc2Zvcm1lZCBkYXRhIGJ5IGNhbGxpbmcgYSBmdW5jdGlvbiBvbiB0aGUgdmFyaWFibGUuCkZvciBleGFtcGxlIGhlcmUgd2Ugc2hvdyB0aGUgbG9nIHRyYW5zZm9ybWVkIGRhdGEuCgpgYGB7cn0KZ2dwbG90KGRzbWFsbCwgYWVzKHggPSBsb2coY2FyYXQpLCB5ID0gbG9nKHByaWNlKSkpICsgZ2VvbV9wb2ludCgpCmBgYAoKIyMgVGV4dCBsYWJlbHMKClVzZSBldmVuIGEgc21hbGxlciBzdWJzZXQgdG8gYXZvaWQgY2x1dHRlcmluZzoKYGBge3J9CnNldC5zZWVkKDEyMzQ1KSAjIE1ha2UgdGhlIHNhbXBsZSByZXByb2R1Y2libGUKZHNtYWxsMiA8LSBkaWFtb25kc1tzYW1wbGUobnJvdyhkaWFtb25kcyksIDEwMCksIF0KYGBgCgpgYGB7cn0KcDIgPC0gZ2dwbG90KGRzbWFsbDIsIGFlcyh4ID0gbG9nKGNhcmF0KSwgeSA9IGxvZyhwcmljZSkpKQpwMiArIGdlb21fdGV4dChhZXMobGFiZWwgPSBjb2xvcikpCmBgYAoKYGBge3J9CnAyICsgZ2VvbV9sYWJlbChhZXMobGFiZWwgPSBjb2xvcikpCmBgYAoKVGhlIGBnZ3JlcGxlbGAgZ2l2ZXMgYW4gZWFzeSB3YXkgdG8gYW5ub3RhdGUgdGhlIGxhYmVscyB3aGVuIHRoZXkgYXJlIGRlbnNlbHkKcGFja2VkLiAKCmBgYHtyfQpsaWJyYXJ5KGdncmVwZWwpCnAyICsgZ2VvbV9wb2ludCgpICsgZ2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbD1jb2xvciksIHNpemUgPSAzKQpgYGAKCkl0IGRvZXNuJ3Qgd29yayB0aGF0IHdlbGwgdGhvdWdoIGlmIHlvdSBoYXZlIHRvbyBtYW55IHBvaW50cyBjbHVzdGVyZWQKdG9nZXRoZXIsIHRoZW4gdGhlIGxpbmVzIHBvaW50aW5nIHRvIHRoZSBwb2ludHMgd2lsbCBleHRlbmQgdG9vIGZhciB3YXksCnRvIG1ha2Ugcm9vbSBmb3IgYWxsIHRoZSBsYWJlbHMuIAoKCmBgYHtyfQpwMSArIGdlb21fcG9pbnQoKSArIGdlb21fdGV4dF9yZXBlbChhZXMobGFiZWw9Y29sb3IpLCBzaXplID0gMykKYGBgCgpJbiB0aGVzZSBjYXNlcyB5b3Ugc2hvdWxkIGNob29zZSB0byBsYWJlbApvbmx5IGEgc3Vic2V0IG9mIHBvaW50cy4KCmBgYHtyfQpzZXQuc2VlZCgxMjM0NTYpCnN1YnNldERhdGEgPC0gc3Vic2V0KGRzbWFsbCwgc2FtcGxlKGMoVFJVRSwgRkFMU0UpLCBucm93KGRzbWFsbCksIHJlcGxhY2UgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9iID0gYygwLjIsIDAuOCkpKQpwMSArIGdlb21fcG9pbnQoKSArIAogIGdlb21fdGV4dF9yZXBlbChkYXRhID0gc3Vic2V0RGF0YSwgYWVzKGxhYmVsPWNvbG9yKSwgc2l6ZSA9IDUsIGNvbCA9ICJCbHVlIikKYGBgCgojIFRoZSBFY29ub21pc3QgRGF0YQoKRm9yIHByYWN0aWNlLCB5b3Ugd2lsbCB0cnkgdG8gcmVjcmVhdGUKYSBwbG90IHB1Ymxpc2hlZCBpbiB0aGUgRWNvbm9taXN0IGlzc3VlIG9mIEp1bHkgMjB0aCwgMjAxNiByZWZsZWN0aW5nCnRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB3ZWxsLWJlaW5nIGFuZCBmaW5hbmNpYWwgaW5jbHVzaW9uLgoKIVtdKC4vZmlndXJlcy9lY29ub21pc3QucG5nKQoKR3JhcGggc291cmNlOiBbaHR0cDovL3d3dy5lY29ub21pc3QuY29tL2Jsb2dzL2dyYXBoaWNkZXRhaWwvMjAxNi8wNy9kYWlseS1jaGFydC0xM10oaHR0cDovL3d3dy5lY29ub21pc3QuY29tL2Jsb2dzL2dyYXBoaWNkZXRhaWwvMjAxNi8wNy9kYWlseS1jaGFydC0xMykKCllvdSB3aWxsIGdlbmVyYXRlIHRoaXMgZmlndXJlIHN0ZXAgYnkgc3RlcCB0aHJvdWdoIGEgc2VyaWVzIG9mIGluY2x1ZGVkIApleGVyY2lzZXMgdXNpbmcgdGhlIHRvb2xzIHdlJ3ZlIGp1c3QgbGVhcm5lZCBhbmQgd2lsbCBsZWFybiBhYm91dC4gCgpUaGUgZGF0YSBmb3IgdGhlIGV4ZXJjaXNlcyBpcyBhdmFpbGFibGUgaW4gdGhlIGBkYXRhU2V0cy9FY29ub21pc3REYXRhLmNzdmAgZmlsZS4gClJlYWQgaXQgaW4gd2l0aCB0aGUgZm9sbG93aW5nIGNvbW1hbmRzOgoKYGBge3J9CmRhdCA8LSByZWFkLmNzdigiLi9kYXRhL0Vjb25vbWlzdERhdGEuY3N2IikKaGVhZChkYXQpCmBgYAoKClRoZSBvcmlnaW5hbCBzb3VyY2VzIGZvciB0aGlzIGRhdGEgYXJlOgoKKiB0aGUgQm9zdG9uIENvbnN1bHRpbmcgR3JvdXDigJlzIFtyZXBvcnQgb24gY291bnRyaWVzJyB3ZWxsLWJlaW5nXShodHRwczovL3d3dy5iY2dwZXJzcGVjdGl2ZXMuY29tL0ltYWdlcy9CQ0ctVGhlLVByaXZhdGUtU2VjdG9yLU9wcG9ydHVuaXR5LXRvLUltcHJvdmUtV2VsbC1CZWluZy1KdWwtMjAxNi5wZGYpLAp3aGljaCBpbmNsdWRlcyBTdXN0YWluYWJsZSBFY29ub21pYyBEZXZlbG9wbWVudCBBc3Nlc3NtZW50IChTRURBKSBzY29yZXMsIAoqcG93ZXJmdWwgZGlhZ25vc3RpY3MgZGVzaWduZWQgdG8gcHJvdmlkZSBnb3Zlcm5tZW50IGxlYWRlcnMgd2l0aCBhIHBlcnNwZWN0aXZlIG9uIGhvdyBlZmZlY3RpdmVseSB0aGVpciBjb3VudHJpZXMgY29udmVydCB3ZWFsdGgsIGFzIG1lYXN1cmVkIGJ5IGluY29tZSBsZXZlbHMsIGludG8gd2VsbC1iZWluZypeW2h0dHBzOi8vd3d3LmJjZ3BlcnNwZWN0aXZlcy5jb20vY29udGVudC9hcnRpY2xlcy9ncm93dGgtZ2xvYmFsaXphdGlvbi1wcml2YXRlLXNlY3Rvci1vcHBvcnR1bml0eS1pbXByb3ZlLXdlbGwtYmVpbmctMjAxNi1lY29ub21pYy1kZXZlbG9wbWVudC1hc3Nlc3NtZW50L10gCiogdGhlIFdvcmxkIEJhbmsgW0dsb2JhbCBGaW5kZXggZGF0YWJhc2VdKGh0dHA6Ly9kYXRhdG9waWNzLndvcmxkYmFuay5vcmcvZmluYW5jaWFsaW5jbHVzaW9uLyksCndoaWNoIHJlY29yZHMgdGhlIGluZGljZXMgb2YgZmluYW5jaWFsIGluY2x1c2lvbiwgaW5jbHVkaW5nIHRoZSBwZXJjZW50Cm9mIHBlb3BsZSBhZ2VkIDE1IG9yIG1vcmUgd2l0aCBhIGJhbmsgYWNjb3VudC4KCgpUaGUgY291bnRyaWVzIGFzc2lnbm1lbnQgdG8gcmVnaW9ucyBpcyBiYXNlZCBvbiB0aGUgRVBJX3JlZ2lvbnMgY29sdW1uIGluIAp0aGUgYGNvdW50cnlFeERhdGFgIGRhdGEuZnJhbWUgZnJvbSBgcndvcmxkbWFwYCBwYWNrYWdlLiBUaGUgYFJlZ2lvbmAgdmFyaWFibGUgCndhcyBtYXRjaGVkIHdpdGggdGhlIGNhdGVnb3JpZXMgaW4gdGhlIEVjb25vbWlzdCBwbG90LiAKCgojIEV4ZXJjaXNlIEkKCkZvciB0aGUgYEVjb25vbWlzdERhdGEuY3N2YCBkbyB0aGUgZm9sbG93aW5nOgoKMS4gQ3JlYXRlIGEgc2NhdHRlciBwbG90IHdpdGggcGVyY2VudCBvZiBwZW9wbGUgb3ZlciB0aGUgYWdlIG9mIDE1IHdpdGggYSBiYW5rIAphY2NvdW50IG9uIHRoZSB4IGF4aXMgYW5kIHRoZSBTRURBIHNjb3JlIG9uIHRoZSB5IGF4aXMuCjIuIENvbG9yIHRoZSBwb2ludHMgaW4gdGhlIHByZXZpb3VzIHBsb3QgYmx1ZS4KMy4gQ29sb3IgdGhlIHBvaW50cyBpbiB0aGUgcHJldmlvdXMgcGxvdCBhY2NvcmRpbmcgdG8gdGhlIGBSZWdpb25gLgo0LiBDcmVhdGUgYm94cGxvdHMgb2YgU0VEQSBzY29yZXMgYnkgYFJlZ2lvbmAuCjUuIE92ZXJsYXkgcG9pbnRzIG9uIHRvcCBvZiB0aGUgYm94IHBsb3RzCgoKYGBge3J9CiMgKC4uLj8pCmBgYAoKCiMgU3RhdGlzdGljYWwgVHJhbnNmb3JtYXRpb25zCgpOb3csIHdlIHdpbGwgZ28gYmFjayB0byB0aGUgYGRpYW1vbmRzYCBkYXRhIHNldCBhbmQgcmV0dXJuIHRvIAp0aGUgRWNvbm9taXN0IHBsb3QgbGF0ZXIuCgpTbyBmYXIgd2UgaGF2ZSBvbmx5IGRlYWx0IHdpdGggdGhlICh4LHkpIHR5cGUgb2YgcGxvdHMgKHNjYXR0ZXIgcGxvdHMgb3IgCmxpbmUgcGxvdHMpIHdoZXJlIGVhY2ggb2YgdGhlIHBvaW50IGhhcyBpdHMgY29ycmVzcG9uZGluZyAoeCx5KSBjb29yZGluYXRlLgoKU29tZXRpbWVzLCBob3dldmVyLCB3ZSBhcmUgbW9yZSBpbnRlcmVzdGVkIGluIHBsb3RzIHRoYXQgcmVxdWlyZSBzb21lIApzdGF0aXN0aWNhbCB0cmFuc2Zvcm1hdGlvbnMuIFRoZSB0cmFuc2Zvcm1hdGlvbnMgbWlnaHQgbWFwIGEgcmF3IGRhdGFwb2ludCBvciAKYSBncm91cCBvZiBkYXRhcG9pbnRzIHRvIG90aGVyIHZhbHVlcy4gRXhhbXBsZXMgb2YgcGxvdHMgaW52b2x2aW5nIHN0YXRpc3RpY2FsIAp0cmFuc2Zvcm1hdGlvbnM6CgoqIGJveHBsb3RzIHdlIGp1c3QgZ2VuZXJhdGVkIGZvciB0aGUgRWNvbm9taXN0IGRhdGEsCiogaGlzdG9ncmFtcwoqIHByZWRpY3Rpb24gbGluZXMgZXRjLgoqIGJhciBjaGFydHMKClRoZXNlIHR5cGVzIG9mIHBsb3RzIHJlcXVpcmUgc29tZSBzdGF0aXN0aWNhbCB0cmFuc2Zvcm1hdGlvbnMuIEZvciBleGFtcGxlOgoKKiBib3hwbG90cyByZXF1aXJlIGNvbXB1dGF0aW9ucyBvZiB0aGUgdGhlIG1lZGlhbiwgbG93ZXIgYW5kIHVwcGVyIHF1YXJ0aWxlLCAKYW5kIDEuNSAqIElRUiBvZiB0aGUgeS12YWx1ZXMsCiogc21vb3RoZXJzIGNvbXB1dGUgdGhlIHByZWRpY3RlZCB2YWx1ZXMgZm9yIHktdmFsdWVzLAoqIGhpc3RvZ3JhbXMgZ3JvdXAgdGhlIHZhbHVlcyBpbnRvIGJpbnMsIAoqIGJhciBjaGFydHMgY291bnRzIHRoZSBjbGFzc2VzIG9jY3VycmVuY2VzLiAKCiMjIEJveHBsb3RzIGFuZCBqaXR0ZXJlZCBwb2ludHMKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBhZXMoeCA9IGNvbG9yLCB5ID1wcmljZS9jYXJhdCkpICsKICBnZW9tX2ppdHRlcigpCmBgYAoKCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTd9CmoxIDwtIGdncGxvdChkYXRhID0gZGlhbW9uZHMsIGFlcyh4ID0gY29sb3IsIHkgPXByaWNlL2NhcmF0KSkgKwogIGdlb21faml0dGVyKGFscGhhID0gSSgxLzUpKQoKajIgPC0gZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgYWVzKHggPSBjb2xvciwgeSA9cHJpY2UvY2FyYXQpKSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSBJKDEvNTApKQoKajMgPC0gZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgYWVzKHggPSBjb2xvciwgeSA9cHJpY2UvY2FyYXQpKSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSBJKDEvMjAwKSkKCmdyaWQuYXJyYW5nZShqMSwgajIsIGozLCBuY29sID0gMykKYGBgCgpIZXJlIHdlIHVzZWQgYGdyaWQuYXJyYW5nZSgpYCBmb3IgdGhlIHBhY2thZ2UgYGdyaWRFeHRyYWAgdG8gZGlzcGxheSAKbXVsdGlwbGUgcGxvdHMgaW4gdGhlIHNhbWUgbGluZS4KClNvbWV0aW1lcyBsZXNzIGlzIG1vcmUuLi4KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBhZXMoeCA9IGNvbG9yLCB5ID1wcmljZS9jYXJhdCkpICsKICBnZW9tX2JveHBsb3QoKQpgYGAKCiMjIEhpc3RvZ3JhbSBhbmQgZGVuc2l0eSBwbG90cwoKQmVsb3cgd2UgcGxvdCB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSB3ZWlnaHRzIChjYXJhdCkgb2YgdGhlIGRpYW1vbmRzLgoKCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTd9CmggPC0gZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgYWVzKHggPSBjYXJhdCkpICsgZ2VvbV9oaXN0b2dyYW0oKQpkIDwtIGdncGxvdChkYXRhID0gZGlhbW9uZHMsIGFlcyh4ID0gY2FyYXQpKSArIGdlb21fZGVuc2l0eSgpCgpncmlkLmFycmFuZ2UoaCwgZCwgbmNvbCA9IDIpCmBgYAoKKiBGb3IgdGhlIGRlbnNpdHkgcGxvdCwgdGhlIGBhZGp1c3RgIGFyZ3VtZW50IGNvbnRyb2xzIHRoZSBkZWdyZWUgb2Ygc21vb3RobmVzcwooaGlnaCB2YWx1ZXMgb2YgYWRqdXN0IHByb2R1Y2Ugc21vb3RoZXIgcGxvdHMpLiAKKiBGb3IgdGhlIGhpc3RvZ3JhbSwgdGhlIGBiaW53aWR0aGAgb3IgYGJpbnNgIGFyZ3VtZW50IGNvbnRyb2xzIHRoZSBhbW91bnQgCm9mIHNtb290aGluZyBieSBzZXR0aW5nIHRoZSBiaW4gc2l6ZSBvciB0aGUgbnVtYmVyIG9mIGJpbnMuIAooQnJlYWsgcG9pbnRzIGNhbiBhbHNvIGJlIHNwZWNpZmllZCBleHBsaWNpdGx5LCB1c2luZyB0aGUgYnJlYWtzIGFyZ3VtZW50LikgCgo8IS0tIEl0IGlzIHZlcnkgaW1wb3J0YW50IHRvIGV4cGVyaW1lbnQgd2l0aCB0aGUgbGV2ZWwgb2Ygc21vb3RoaW5nLiBXaXRoIGEgaGlzdG9ncmFtIC0tPgo8IS0tIHlvdSBzaG91bGQgdHJ5IG1hbnkgYmluIHdpZHRoczogWW91IG1heSBmaW5kIHRoYXQgZ3Jvc3MgZmVhdHVyZXMgb2YgdGhlIGRhdGEgLS0+CjwhLS0gc2hvdyB1cCB3ZWxsIGF0IGEgbGFyZ2UgYmluIHdpZHRoLCB3aGlsZSBmaW5lciBmZWF0dXJlcyByZXF1aXJlIGEgdmVyeSBuYXJyb3cgLS0+CjwhLS0gd2lkdGguIC0tPgoKYGBge3IgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9N30KcCA8LSBnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBhZXMoeCA9IGNhcmF0KSkgKyB4bGltKDAsIDMpCgpoMSA8LSBwICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKSAKaDIgPC0gcCArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4xKSAKaDMgPC0gcCArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4wMSkgCgpncmlkLmFycmFuZ2UoaDEsIGgyLCBoMywgbmNvbCA9IDMpCmBgYAoKYGBge3IgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9N30KZDEgPC0gcCArIGdlb21fZGVuc2l0eShhZGp1c3QgPSA1KSAKZDIgPC0gcCArIGdlb21fZGVuc2l0eShhZGp1c3QgPSAxKSAKZDMgPC0gcCArIGdlb21fZGVuc2l0eShhZGp1c3QgPSAxLzUpIAoKZ3JpZC5hcnJhbmdlKGQxLCBkMiwgZDMsIG5jb2wgPSAzKQpgYGAKClRoZSBoaXN0b2dyYW1zIGNhbiBiZSBicm9rZW4gZG93biBpbnRvIGdyb3Vwcy4gSGVyZSB3ZSBzaG93IGdyb3VwaW5nIGJ5IGRpYW1vbmRzCmN1dC4KCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTd9CmggPC0gcCArIGdlb21faGlzdG9ncmFtKGFlcyhmaWxsID0gY3V0KSwgcG9zaXRpb24gPSAiZG9kZ2UiLCBiaW5zID0gMTApCmQgPC0gcCArIGdlb21fZGVuc2l0eShhZXMoY29sb3IgPSBjdXQpKQoKZ3JpZC5hcnJhbmdlKGgsIGQsIG5jb2wgPSAyKQpgYGAKCkluc3RlYWQgb2YgdGhlIG1hcmdpbmFsIGRpc3RyaWJ1dGlvbiwgd2UgY2FuIHBsb3QgdGhlIGNvbXBvbmVudHMgKipzdGFja2VkKioKb24gdG9wIG9mIGVhY2ggb3RoZXIgdG8gc2VlIHRoZSBjb250cmlidXRpb24gZnJvbSBlYWNoIG9mIGdyb3VwLgoKYGBge3IgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9N30KaCA8LSBwICsgZ2VvbV9oaXN0b2dyYW0oYWVzKGZpbGwgPSBjdXQpLCBwb3NpdGlvbiA9ICJzdGFjayIpCmQgPC0gcCArIGdlb21fZGVuc2l0eShhZXMoZmlsbCA9IGN1dCksIHBvc2l0aW9uID0gInN0YWNrIikKCmdyaWQuYXJyYW5nZShoLCBkLCBuY29sID0gMikKYGBgCgojIyBCYXIgY2hhcnRzCgpUaGUgZGlzY3JldGUgYW5hbG9ndWUgb2YgaGlzdG9ncmFtIGlzIHRoZSBiYXIgY2hhcnQsIGBnZW9tID0gImJhciJgLiAKSW5zdGVhZCBvZiBwYXJ0aXRpb25pbmcgdGhlIHZhbHVlcyBpbnRvIGJpbnMgbGlrZSBoaXN0b2dyYW1zLCB0aGUgYmFyCmdlb20gY291bnRzIHRoZSBudW1iZXIgb2YgaW5zdGFuY2VzIG9mIGVhY2ggZGlzY3JldGUgY2xhc3MuIFRoZSBjb3VudHMKYXJlIHRoZW4gcGxvdHRlZCBhcyBjb2x1bW5zIGZvciBlYWNoIGRpc3RpbmN0IGNsYXNzLgoKSWYgeW914oCZZCBsaWtlIHRvIHRhYnVsYXRlIGNsYXNzIG1lbWJlcnMgaW4gc29tZSBvdGhlciB3YXkgKHJhdGhlciB0aGFuIGNvdW50KSwgCmUuZy4gYnkgc3VtbWluZyB1cCBhIGNvbnRpbnVvdXMgdmFyaWFibGUsIHlvdSBjYW4gdXNlIHRoZSBgd2VpZ2h0YCBhZXN0aGV0aWMuIAoKYGBge3IgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9Ni44fQpiMSA8LSBnZ3Bsb3QoZGlhbW9uZHMsIGFlcyh4ID0gY29sb3IpKSArIGdlb21fYmFyKCkKYjIgPC0gZ2dwbG90KGRpYW1vbmRzLCBhZXMoeCA9IGNvbG9yKSkgKyBnZW9tX2JhcihhZXMod2VpZ2h0ID0gY2FyYXQpKSArIHlsYWIoImNhcmF0IikKZ3JpZC5hcnJhbmdlKGIxLCBiMiwgbmNvbCA9IDIpCmBgYAoKVGhlIGxlZnQgcGxvdCBzaG93cyBjb3VudHMgYW5kIHRoZSByaWdodCBwbG90IGlzIHRoZSBjb3VudCB3ZWlnaHRlZCBieSAKYHdlaWdodCA9IGNhcmF0YCB0byBzaG93IHRoZSB0b3RhbCB3ZWlnaHQgb2YgZGlhbW9uZHMgb2YgZWFjaCBjb2xvci4KCgpUaHVzLCB5b3UgZG9u4oCZdCBuZWVkIHRvIHRhYnVsYXRlIHlvdXIgdmFsdWVzIGJlZm9yZWhhbmQsIGFzIHdpdGggYGJhcmNoYXJ0YCAKaW4gYmFzZSBSLiBIb3dldmVyLCBpZiB5b3UgZGlkIGFscmVhZHkgc3VtbWFyaXplIHlvdXIgZGF0YSwgeW91IGNhbiBzdGlsbCB1c2UKYGdlb21fYmFyYCBidXQgdXNpbmcgYW5vdGhlciBzdGF0aXN0aWNhbCB0cmFuc2Zvcm1hdGlvbiBgc3RhdCA9ICJpZGVudGl0eWAKcmF0aGVyIHRoYW4gdGhlIGRlZmF1bHQgYHN0YXQgPSAiY291bnQiYC4KCmBgYHtyfQpkaWFtb25kcy5tZWFuIDwtIGFnZ3JlZ2F0ZShkaWFtb25kc1siY2FyYXQiXSwgZGlhbW9uZHNbImNvbG9yIl0sIEZVTj1tZWFuKQpyYmluZChoZWFkKGRpYW1vbmRzLm1lYW4pLCB0YWlsKGRpYW1vbmRzLm1lYW4pKQpgYGAKClRoZSBkZWZhdWx0IG9wdGlvbiB3aWxsIGdlbmVyYXRlIGFuIGVycm9yOgoKYGBge3J9CmdncGxvdChkaWFtb25kcy5tZWFuLCBhZXMoeD1jb2xvciwgeT1jYXJhdCkpICsgCiAgZ2VvbV9iYXIoKQpgYGAKClRodXMsIHlvdSBuZWVkIHRvIHVzZSB0aGUgZm9sbG93aW5nOgoKYGBge3J9CmdncGxvdChkaWFtb25kcy5tZWFuLCBhZXMoeD1jb2xvciwgeT1jYXJhdCkpICsgCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKQpgYGAKCmBgYHtyfQpkaWFtb25kcy5zdW0gPC0gYWdncmVnYXRlKGRpYW1vbmRzWyJjYXJhdCJdLCBkaWFtb25kc1siY29sb3IiXSwgRlVOPXN1bSkKZ2dwbG90KGRpYW1vbmRzLnN1bSwgYWVzKHg9Y29sb3IsIHk9Y2FyYXQpKSArICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpCmBgYAoKTm90ZSB0aGF0IHRoaXMgaXMgdGhlIHNhbWUgcGxvdCBhcyB0aGUgb25lIGdlbmVyYXRlZCB3aXRoIHRoZSBgd2VpZ2h0YAphZXN0aGV0aWMsIHdoaWNoIGlzIGV4YWN0bHkgd2hhdCB3ZSBzaG91bGQgZXhwZWN0LgoKCiMjIFByZWRpY3Rpb24gbGluZXMKCldlIGNhbiBpbmNsdWRlIGEgcmVncmVzc2lvbiBsaW5lIHRvIHBsb3QgYnkgc2ltcGx5CmFkZGluZyB0aGUgbGluZSB3aXRoIHRoZSBmaXR0ZWQgeS12YWx1ZXMgZnJvbSBhIHByZWRpY3Rpb24gbW9kZWw6CgpgYGB7cn0KZHNtYWxsJHByZWQucHJpY2UgPC0gcHJlZGljdChsbShwcmljZSB+IGNhcmF0LCBkYXRhID0gZHNtYWxsKSkKcDEgPC0gZ2dwbG90KGRzbWFsbCwgYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlKSkKcDEgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNvbG9yKSkgKyBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkLnByaWNlKSkKYGBgCgoKIyMgU21vb3RoZXJzCgpJZiB5b3UgaGF2ZSBhIHNjYXR0ZXJwbG90IHdpdGggbWFueSBkYXRhIHBvaW50cywgaXQgY2FuIGJlIGhhcmQgdG8gc2VlIGV4YWN0bHkKd2hhdCB0cmVuZCBpcyBzaG93biBieSB0aGUgZGF0YS4gSW4gdGhpcyBjYXNlIHlvdSBtYXkgd2FudCB0byBhZGQgYSBzbW9vdGhlZApsaW5lIHRvIHRoZSBwbG90LiBUaGUgc21vb3RoIGdlb20gaW5jbHVkZXMgYSBsaW5lIGFuZCBhIHJpYmJvbi4KCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlKSkgKwogIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkKYGBgCgpGb3Igb3VyIHNtYWxsIHN1YnNldCBvZiB0aGUgZGlhbW9uZHMgZGF0YSBzZXQgd2UgaGF2ZToKCmBgYHtyfQpwMSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkKYGBgCgpDaGFuZ2luZyB0aGUgYHNwYW5gIGFyZ3VtZW50LCB3ZSBjYW4gb2J0YWluIG1vcmUgb3IgbGVzcyB3aWdnbHkgY3VydmUKKHNtYWxsZXIgYHNwYW5gIHJlc3VsdHMgaW4gbW9yZSB3aWdnbGluZXNzKS4KCmBgYHtyfQpncmlkLmFycmFuZ2UocDEgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChzcGFuID0gMC4yKSwKICAgICAgICAgICAgIHAxICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoc3BhbiA9IDAuNyksIG5jb2wgPSAyKQpgYGAKCgoqIFRoZSBkZWZhdWx0IG1ldGhvZCB1c2VkIGluIGBnZW9tX3Ntb290aGAgd2l0aCBzbWFsbCBudW1iZXIKb2Ygb2JzZXJ2YXRpb25zIChgbiA8IDEwMDBgKSBpcyBgbWV0aG9kID0gImxvZXNzImAgd2hpY2ggdXNlcyBhIHNtb290aCBsb2NhbCAKcmVncmVzc2lvbi4gTW9yZSBkZXRhaWxzIGFib3V0IHRoZSBhbGdvcml0aG0gdXNlZCBjYW4gYmUgZm91bmQgaW4gYD9sb2Vzc2AuIAoqIExvZXNzIGRvZXMgbm90IHdvcmsgd2VsbCBmb3IgbGFyZ2UgZGF0YXNldHMgKGl04oCZcyAkTyhuXjIpJCBpbiBtZW1vcnkpLCBhbmQgc28KYW4gYWx0ZXJuYXRpdmUgc21vb3RoaW5nIGFsZ29yaXRobSBpcyB1c2VkIHdoZW4gbiBpcyBncmVhdGVyIHRoYW4gMSwwMDAuCgoKIyBFeGVyY2lzZSBJSQoKMS4gUmUtY3JlYXRlIGEgc2NhdHRlciBwbG90IHdpdGggcGVyY2VudCBvZiBwZW9wbGUgYWdlZCAxNSsgd2l0aCBhIGJhbmsgYWNjb3VudApvbiB0aGUgeCBheGlzIGFuZCBTRURBIGN1cnJlbnQgbGV2ZWwgc2NvcmUgb24gdGhlIHkgYXhpcyAKKGFzIHlvdSBkaWQgaW4gdGhlIHByZXZpb3VzIGV4ZXJjaXNlKS4KMi4gT3ZlcmxheSBhIHNtb290aGluZyBsaW5lIG9uIHRvcCBvZiB0aGUgc2NhdHRlciBwbG90IHVzaW5nIHRoZSBsbSBtZXRob2QuIApIaW50OiBzZWUgYD9zdGF0X3Ntb290aGAuCjMuIE92ZXJsYXkgYSBzbW9vdGhpbmcgbGluZSBvbiB0b3Agb2YgdGhlIHNjYXR0ZXIgcGxvdCB1c2luZyB0aGUgZGVmYXVsdCBtZXRob2QuCjQuIE92ZXJsYXkgYSBzbW9vdGhpbmcgbGluZSBvbiB0b3Agb2YgdGhlIHNjYXR0ZXIgcGxvdCB1c2luZyB0aGUgZGVmYXVsdCBsb2VzcyAKbWV0aG9kLCBidXQgbWFrZSBpdCBsZXNzIHNtb290aC4gSGludDogc2VlIGA/bG9lc3NgLgoKYGBge3J9CiMgKC4uLj8pCmBgYAoKCiMgU2NhbGVzCgoKIyMgQWVzdGhldGljIE1hcHBpbmcgVmFyaWFibGUgU2NhbGluZwoKQWVzdGhldGljIG1hcHBpbmcgKGkuZS4sIHdpdGggYWVzKCkpIGlzIHJlc3BvbnNpYmxlIGZvciBhc3NpZ25pbmcgYW4gYWVzdGhldGljIAp0byBhIHZhcmlhYmxlLiBJdCBkb2Vzbid0IGhvd2V2ZXIgc3BlY2lmeSBob3cgbWFwcGluZyBzaG91bGQgYmUgZG9uZS4gCgpGb3IgZXhhbXBsZSwgYGFlcyhzaGFwZSA9IHgpYCBvciBgYWVzKGNvbG9yID0geilgIGRvIG5vdCBzcGVjaWZ5IHdoYXQgc2hhcGVzCm9yIHdoYXQgY29sb3JzIHNob3VsZCBiZSB1c2VkLiBUbyBjaG9vc2UgY29sb3JzL3NoYXBlcy9zaXplcyBldGMuIHlvdSBuZWVkCnRvIG1vZGlmeSB0aGUgY29ycmVzcG9uZGluZyBzY2FsZS4gCgoqKkluIGdncGxvdDIgc2NhbGVzIGluY2x1ZGU6KioKCiogcG9zaXRpb24KKiBjb2xvciBhbmQgZmlsbAoqIHNpemUKKiBzaGFwZQoqIGxpbmUgdHlwZQoKU2NhbGVzIGFyZSBtb2RpZmllZCB3aXRoIGEgc2VyaWVzIG9mIGZ1bmN0aW9ucyB1c2luZyBhIApgc2NhbGVfPGFlc3RoZXRpYz5fPHR5cGU+YCBuYW1pbmcgc2NoZW1lLiBUcnkgdHlwaW5nIGBzY2FsZV88dGFiPmAgdG8gc2VlIAphIGxpc3Qgb2Ygc2NhbGUgbW9kaWZpY2F0aW9uIGZ1bmN0aW9ucy4KCioqQ29tbW9uIFNjYWxlIEFyZ3VtZW50czoqKgoKKiAqKm5hbWUqKjogdGhlIGZpcnN0IGFyZ3VtZW50IGdpdmVzIHRoZSBheGlzIG9yIGxlZ2VuZCB0aXRsZQoqICoqbGltaXRzKio6IHRoZSBtaW5pbXVtIGFuZCBtYXhpbXVtIG9mIHRoZSBzY2FsZQoqICoqYnJlYWtzKio6IHRoZSBwb2ludHMgYWxvbmcgdGhlIHNjYWxlIHdoZXJlIGxhYmVscyBzaG91bGQgYXBwZWFyCiogKipsYWJlbHMqKjogdGhlIGxhYmVscyB0aGF0IGFwcGVhciBhdCBlYWNoIGJyZWFrCgojIyBTY2FsZTogYXhlcwoKU3F1YXJlIHJvb3QgdHJhbnNmb3JtYXRpb24gb2YgdGhlIHktYXhpczoKCmBgYHtyfQpwMSA8LSBnZ3Bsb3QoZHNtYWxsLCBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKSAKcDEgKyBnZW9tX3BvaW50KCkgKyBzY2FsZV95X3NxcnQoKQpgYGAKCkxvZyBiYXMgMTAgdHJhbnNmb3JtYXRpb24gb2YgdGhlIHktYXhpczoKCmBgYHtyfQpwMSArIGdlb21fcG9pbnQoKSArIHNjYWxlX3lfbG9nMTAoKQpgYGAKCkxvZyBiYXNlIDEwIHRyYW5zZm9ybWF0aW9uIG9mIHggYW5kIHkgYXhlczoKCmBgYHtyfQpwMSArIGdlb21fcG9pbnQoKSArIHNjYWxlX3lfbG9nMTAoKSArIHNjYWxlX3hfbG9nMTAoKQpgYGAKCk5vdGUgdGhhdCB0aGUgYWJvdmUgcHJvZHVjZXMgdGhlIHNhbWUgcG9pbnRzIGFzOgpgYGB7cn0KZ2dwbG90KGRzbWFsbCwgYWVzKHggPSBsb2coY2FyYXQpLCB5ID0gbG9nKHByaWNlKSkpICsgZ2VvbV9wb2ludCgpCmBgYAoKYnV0IHdpdGggZGlmZmVyZW50IHZhbHVlcyBvbiB0aGUgYXhlcy4KCiMjIFNjYWxlOiBzaGFwZXMKCmBgYHtyfQpwMSArIGdlb21fcG9pbnQoYWVzKHNoYXBlID0gY3V0KSwgc2l6ZSA9IDMpICAKYGBgCgpgYGB7cn0KcDEgKyBnZW9tX3BvaW50KGFlcyhzaGFwZSA9IGN1dCksIHNpemUgPSAzKSArIAogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKDE6NSkpCmBgYAoKIyMgU2NhbGU6IGNvbG9ycwoKVG8gY2hvb3NlIHNwZWNpZmljIGNvbG9ycyBmb3IgKipkaXNjcmV0ZSoqIHZhcmlhYmxlcyB3ZSBjYW4gdXNlIGBzY2FsZV9jb2xvcl9tYW51YWxgCgpgYGB7cn0KcDEgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGN1dCksIHNpemUgPSAzKSArIAogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJyZWQiLCAib3JhbmdlIiwgInllbGxvdyIsICJncmVlbiIsICJibHVlIikpCmBgYAoKRm9yICoqY29udGludW91cyoqIHZhcmlhYmxlcyB5b3UgY2FuIGFsc28gdXNlIGBzY2FsZV9jb2xvcl9ncmFkaWVudGAsIGFuZCBzcGVjaWZ5CnRoZSBlbmRzIG9mIHRoZSBzcGVjdHJ1bToKCmBgYHtyfQpwMSArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gcHJpY2UpLCBzaXplID0gMykgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudChsb3cgPSAiYmx1ZSIsIGhpZ2ggPSAicmVkIikKYGBgCgpgc2NhbGVfY29sb3JfYnJld2VyYCBpcyBhIHZlcnkgdXNlZnVsIGZ1bmN0aW9uIHRoYXQgY2FuIGJlIHVzZWQgdG8gc2V0CmNvbG9ycyBmb3IgKipkaXNjcmV0ZSoqIHZhcmlhYmxlcy4gSXQgZ2l2ZXMgeW91IGEgY2hvaWNlIGJldHdlZW4gbWFueSBwcmVkZWZpbmVkCmFuZCBwcmV0dHkgY29sb3IgcGFsZXR0ZXMuCgpgYGB7cn0KcDEgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGN1dCksIHNpemUgPSAzKSArIAogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlNldDIiKQpgYGAKClVuZm9ydHVuYXRlbHkgYHNjYWxlX2NvbG9yX2JyZXdlcmAgZG9lc24ndCB3b3JrIGZvciBjb250aW51b3VzIHZhcmlhYmxlczoKCmBgYHtyfQpwMSArIGdlb21fcG9pbnQoYWVzKHNoYXBlID0gcHJpY2UpLCBzaXplID0gMykgKyAKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJTcGVjdHJhbCIpCmBgYAoKVGhhbmtmdWxseSwgd2UgY2FuIGdldCBhcm91bmQgdGhpcyBpc3N1ZSB1c2luZyB0aGUgYFJDb2xvckJyZXdlcmAgcGFja2FnZQphbmQgdXNpbmcgYHNjYWxlX2NvbG9yX2dyYWRpZW50bmA6CgpgYGB7cn0KbGlicmFyeShSQ29sb3JCcmV3ZXIpCnAxICsgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBwcmljZSksIHNpemUgPSAzKSArIAogIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvdXJzID0gYnJld2VyLnBhbChuYW1lID0gIlNwZWN0cmFsIiwgbiA9IDEwKSkKYGBgCgoKLi4uIGFuZCBpZiB5b3UgYXJlIGEgdHJ1ZSBpbmRpZSBwZXJzb24sIHlvdSBjYW4gaXMgZXZlbiBjaGVjayBvdXQKdGhlIGNvbG9yIHNjaGVtZXMgYmFzZWQgb24gW1dlcyBBbmRlcnNvbl0oaHR0cDovL3dlc2FuZGVyc29ucGFsZXR0ZXMudHVtYmxyLmNvbS8pCm1vdmllczoKCmBgYHtyfQojaW5zdGFsbC5wYWNrYWdlcygid2VzYW5kZXJzb24iKQpsaWJyYXJ5KHdlc2FuZGVyc29uKQpuYW1lcyh3ZXNfcGFsZXR0ZXMpCmBgYAoKRm9yIGRpc2NyZXRlOgoKYGBge3J9CnAxICsgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBjdXQpLCBzaXplID0gMykgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gd2VzX3BhbGV0dGUoIkRhcmplZWxpbmciLCBuID0gNSkpCmBgYAoKRm9yIGNvbnRpbnVvdXM6CgpgYGB7cn0KcDEgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IHByaWNlKSwgc2l6ZSA9IDMpICsgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG91cnMgPSB3ZXNfcGFsZXR0ZSgiRGFyamVlbGluZyIsIDEwMCwgdHlwZSA9ICJjb250aW51b3VzIikpCmBgYAoKCgpZb3UgY2FuIGFsc28gc2NhbGUgdGhlIHZhbHVlcyBvZiB0aGUgdmFyaWFibGUgY29ycmVzcG9uZGluZyB0byBjb2xvci4KCmBgYHtyfQpwMSArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gcHJpY2UpLCBzaXplID0gMykgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudChsb3cgPSAiYmx1ZSIsIGhpZ2ggPSAicmVkIiwgdHJhbnMgPSAibG9nMTAiKQpgYGAKCmFuZCBhZGQgeW91ciBvd24gYnJlYWtzIAoKCmBgYHtyfQpwMSArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gcHJpY2UpLCBzaXplID0gMykgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudChsb3cgPSAiYmx1ZSIsIGhpZ2ggPSAicmVkIiwgdHJhbnMgPSAibG9nMTAiLAogICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoMTAwMCwgMjAwMCwgNTAwMCwgMTAwMDApLAogICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIiAgMTAwMCIsICIgIDIwMDAiLCAiICA1MDAwIiwgIjEwMDAwIikpIAogIApgYGAKCgojIEV4ZXJjaXNlIElJSQoKMS4gRm9yIHRoZSBzY2F0dGVyIHBsb3Qgb2YgJSBvZiBwcGwgYWdlZCAxNSsgd2l0aCBiYW5rIGFjY291bnQgdnMgU0VEQSBzY29yZQpjb2xvcmVkIGJ5IHJlZ2lvbiwgZ2VuZXJhdGVkIGluIEV4ZXJjaXNlIEkuMyBtb2RpZnkgdGhlIGNvbG9yIHNjYWxlIHRvIAp1c2Ugc3BlY2lmaWMgdmFsdWVzIG9mIHlvdXIgY2hvb3NpbmcuIEhpbnQ6IHNlZSBgP3NjYWxlX2NvbG9yX21hbnVhbGAuCgpgYGB7cn0KIyAoLi4uPykKYGBgCgojIEZhY2V0aW5nCgpgYGB7cn0KcCA8LSAgZ2dwbG90KGRpYW1vbmRzLCBhZXMoeCA9IGNhcmF0KSkKcCArIGdlb21faGlzdG9ncmFtKCkKYGBgCgpgYGB7cn0KcCArIGdlb21faGlzdG9ncmFtKCkgKyBmYWNldF93cmFwKH4gY29sb3IpCmBgYAoKYGBge3IgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9Nn0KcCArIGdlb21faGlzdG9ncmFtKCkgKyBmYWNldF9ncmlkKGN1dCB+IGNvbG9yKQpgYGAKCmBgYHtyfQoocCA8LSBnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzW3NhbXBsZShucm93KGRpYW1vbmRzKSwgMTAwMCksIF0sIAogICAgICAgICAgICAgYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlKSkgKwogIGdlb21fcG9pbnQoYWVzKHRleHQgPSBwYXN0ZSgiQ2xhcml0eToiLCBjbGFyaXR5KSksIHNpemUgPSAxKSArCiAgZ2VvbV9zbW9vdGgoYWVzKGNvbG91ciA9IGN1dCwgZmlsbCA9IGN1dCkpICsgZmFjZXRfd3JhcCh+IGN1dCkpCmBgYAoKCiMgRXhjZXJjaXNlIElWCjEuIEZhY2V0ICBieSByZWdpb24gKGB+IFJlZ2lvbmApIHRoZSB0aGUgRWNvbm9taXN0IHBsb3QgZnJvbSBFeGVyY2lzZSBJSUkuCgpgYGB7cn0KIyAoLi4uID8pCmBgYAoKCiMgRmluaXNoIHRoZSBFY29ub21pc3QgcGxvdC4KClRvIGNvbXBsZXRlIHRoZSBncmFwaCB3ZSBuZWVkIHRvOgoKKiBhZGQgYSB0cmVuZCBsaW5lCiogY2hhbmdlIHRoZSBheGlzIGxhYmVscwoqIGNoYW5nZSB0aGUgb3JkZXIgb2YgdGhlIFJlZ2lvbiBsYWJlbHMKKiBjaGFuZ2UgdGhlIGNvbG9yaW5nIG9mIHRoZSBwb2ludHMKKiBsYWJlbCBzZWxlY3RlZCBwb2ludHMKKiBjaGFuZ2UgY29sb3IgbGVnZW5kJ3MgcG9zaXRpb24KKiBhZGp1c3QgdGhlIGF4ZXMgcmF0aW8KKiBmaXggdGhlIHRpY2sgbWFya3MKKiBtYXRjaCB0aGUgcGxvdCdzIHRoZW1lIHdpdGggdGhlIEVjb25vbWlzdCB0aGVtZQoqIGFkZCBub3RlcwoKIyMgQ2hhbmdlIG9yZGVyIG9mIHRoZSBSZWdpb25zCgpgYGB7cn0KZGF0JFJlZ2lvbiA8LSBhcy5jaGFyYWN0ZXIoZGF0JFJlZ2lvbikKZGF0JFJlZ2lvbiA8LSBmYWN0b3IoZGF0JFJlZ2lvbiwgCiAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIkV1cm9wZSIsICJBc2lhIiwgIk9jZWFuaWEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTm9ydGggQW1lcmljYSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMYXRpbiBBbWVyaWNhICYgdGhlIENhcmliYmVhbiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNaWRkbGUgRWFzdCAmIE5vcnRoIEFmcmljYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlN1Yi1TYWhhcmFuIEFmcmljYSIpLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJFdXJvcGUiLCAiQXNpYSIsICJPY2VhbmlhIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5vcnRoIEFtZXJpY2EiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTGF0aW4gQW1lcmljYSAmIFxuIHRoZSBDYXJpYmJlYW4iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTWlkZGxlIEVhc3QgJiBcbiBOb3J0aCBBZnJpY2EiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTdWItU2FoYXJhbiBcbiBBZnJpY2EiKSkKYGBgCgpgYGB7cn0KZ2dwbG90KGRhdCwgYWVzKFBlcmNlbnQub2YuMTVwbHVzLndpdGguYmFuay5hY2NvdW50LCBTRURBLkN1cnJlbnQubGV2ZWwpKSArIAogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gUmVnaW9uKSkKYGBgCgojIEFkZCB0aGUgbGluZWFyIHRyZW5kCgpgYGB7cn0KIyAoLi4uPykKYGBgCgojIyBDaGFuZ2UgdGhlIGF4ZXMgcmF0aW8uCgpIaW50IGA/Y29vcmRfZml4ZWRgCgpgYGB7cn0KIyAoLi4uPykKYGBgCgojIENoYW5nZSB0aGUgY29sb3Igc2NoZW1lCgpVc2UgdGhlIGZvbGxvd2luZyBjb2xvcnMgCmBjKCIjMjhBQURDIiwiI0YyNTgzRiIsICIjNzZDMEMxIiwiIzI0NTc2RCIsICIjMjQ4RTg0IiwiI0RDQzNBQSIsICIjOTY1MDNGIilgCgpgYGB7cn0KIyAoLi4uPykKYGBgCgoKIyMgQWRkIGEgdGl0bGUgYW5kIGZvcm1hdCB0aGUgYXhlcwoKQ2hlY2sgYD9zY2FsZV94X2NvbnRpbnVvdXNgIGFuZCBgP3NjYWxlX3lfY29udGludW91c2AKZG9jdW1lbnRhdGlvbi4gVG8gYWRkIGEgdGl0bGUgdXNlIGBnZ3RpdGxlKClgLgpgYGB7cn0KIyAoLi4uPykKYGBgCgojIyBDaGFuZ2UgdGhlIGJhY2tncm91bmQgYW5kIHRoZW1lCgpZb3UgY2FuIGNoZWNrIG91dCB0aGUgW2BnZ3RoZW1lc2BdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9nZ3RoZW1lcy92aWduZXR0ZXMvZ2d0aGVtZXMuaHRtbCkgCnBhY2thZ2Ugd2hpY2ggaW1wbGVtZW50IHRoZSB0aGVtZXMgdGhhdCBtYWtlIHlvdXIgcGxvdHMgbG9vayBsaWtlIHRoZXkgY2FtZSBmcm9tOgoKKiBCYXNlIGdyYXBoaWNzCiogVGFibGVhdQoqIEV4Y2VsCiogU3RhdGEKKiB0aGUgRWNvbm9taXN0CiogV2FsbCBTdHJlZXQgSm91cm5hbAoqIEVkd2FyZCBUdWZ0ZQoqIE5hdGUgU2lsdmVyJ3MgRml2ZXRoaXJ0eWVpZ2h0CiogZXRjLgoKdXNlIHRoZSBvbmUgdGhhbiBtaW1pY3MgdGhlIEVjb25vbWlzdC4KCmBgYHtyfQpsaWJyYXJ5KGdndGhlbWVzKQojICguLi4/KQpgYGAKCiMjIEZvcm1hdCB0aGUgbGVnZW5kCgpVc2luZyBgdGhlbWUoKWAgYW5kIHRoZSBhcmd1bWVudHMgbGlrZTogCmBsZWdlbmQucG9zaXRpb25gLCBgbGVnZW5kLmRpcmVjdGlvbmAsIGBsZWdlbmQudGV4dGAsIGBwbG90Lm1hcmdpbmAKCmBgYHtyLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD01fQojICguLi4/KQpgYGAKCgojIyBBZGQgcG9pbnQgbGFiZWxzCgpBZGQgbGFiZWxzIHRvIHRoZSBmb2xsb3dpbmcgc3Vic2V0IG9mIHRoZSBjb3VudHJpZXM6CgpgYGB7cn0KcG9pbnRzVG9MYWJlbCA8LSBjKCJZZW1lbiIsICJJcmFxIiwgIkVneXB0IiwgIkpvcmRhbiIsICJDaGFkIiwgIkNvbmdvIiwgCiAgICAgICAgICAgICAgICAgICAiQW5nb2xhIiwgIkFsYmFuaWEiLCAiWmltYmFid2UiLCAiVWdhbmRhIiwgIk5pZ2VyaWEiLAogICAgICAgICAgICAgICAgICAgIlVydWd1YXkiLCAiS2F6YWtoc3RhbiIsICJJbmRpYSIsICJUdXJrZXkiLCAiU291dGggQWZyaWNhIiwKICAgICAgICAgICAgICAgICAgICJLZW55YSIsICJSdXNzaWEiLCAiQnJhemlsIiwgIkNoaWxlIiwgIlNhdWRpIEFyYWJpYSIsIAogICAgICAgICAgICAgICAgICAgIlBvbGFuZCIsICJDaGluYSIsICJTZXJiaWEiLCAiVW5pdGVkIFN0YXRlcyIsICJVbml0ZWQgS2luZ2RvbSIpCmBgYAoKVXNlIGBnZW9tX3RleHRfcmVwZWwoKWAKCmBgYHtyLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD01fQojICguLi4/KQpgYGAKCiMjIEFkZCBub3RlcyB0byB0aGUgYm90dG9tIGFuZCBzYXZlIHRoZSBwbG90CgpVc2UgYGdyaWQudGV4dCgpYAoKYGBge3J9CiMgKC4uLj8pCmBgYAoKCgojIGBwbG90bHlgIGZvciBpbnRlcmFjdGl2ZSBwbG90dGluZwoKWW91IGNhbiBhbHNvIGVhc2lseSBnZW5lcmF0ZSBhbiBpbnRlcmFjdGl2ZSBwbG90IGJ5IGNhbGxpbmcgYGdncGxvdGx5KClgCmZ1bmN0aW9uIGZyb20gYHBsb3RseWAgcGFja2FnZS4KCmBgYHtyfQojaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5IikKYGBgCgoKYGBge3IsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTV9CmxpYnJhcnkocGxvdGx5KQojZ2dwbG90bHkocEVjb25vbWlzdCkKYGBgCgoKU2ltaWxhciB0byB0aGUgb3JpZ2luYWw6CgohW10oLi9maWd1cmVzL2Vjb25vbWlzdC5wbmcpCgoKIyBXaGF0IHdlIGhhdmUgbGVhcm5lZCBzbyBmYXI6CgoqIDJEIHBsb3R0aW5nIGluIFIgY2FuIGJlIGRvbmUgd2l0aDoKICAgICsgYmFzZSBncmFwaGljcyBvciBgZ2dwbG90MmAKKiBUaGUgYnVpbGRpbmcgYmxvY2tzIG9mIGBnZ3Bsb3QyYAoqIEhvdyB0byBnZW5lcmF0ZToKICAgICsgbGluZSBwbG90cwogICAgKyBzY2F0dGVyIHBsb3RzCiAgICArIGhpc3RvZ3JhbXMKICAgICsgYmFyIHBsb3RzCiAgICArIGJveHBsb3RzCiAgICArIHByZWRpY3Rpb24vdHJlbmQgbGluZXMgb3Igc21vb3RoZXJzCiogSG93IHRvIG1vZGlmeSB0aGUgYWVzdGhldGljcyBzZXR0aW5nczoKICAgICsgY29sb3Jpbmcgc2NoZW1lCiAgICArIHNoYXBlcwoqIEhvdyB0byB1c2UgdGhlbWVzIHRvIGF1dG9tYXRpY2FsbHkgY2hhbmdlIHRoZSBzdHlsZSBvZiBhIHBsb3QuCiogRmFjZXQgdGhlIHBsb3QgdG8gZGlzcGxheSB0aGUgaW5mb3JtYXRpb24gZm9yIGRpZmZlcmVudCBzdWJzZXRzIG9mIGRhdGEgd2l0aCAKZGlmZmVyZW50IHZhbHVlcyBvZiBhIHNwZWNpZmljIGF0dHJpYnV0ZS4KCiMgQWRkaXRpb25hbCByZXNvdXJjZXM6CgoqIEhhZGxleSBXaWNraGFtJ3MgW1IgZm9yIERhdGEgU2NpZW5jZV0oaHR0cDovL3I0ZHMuaGFkLmNvLm56Lyk6IENoYXB0ZXIgMy4gRGF0YSBWaXN1YWxpemF0aW9uCiogSGFkbGV5IFdpY2toYW0ncyBbZ2dwbG90MjogRWxlZ2FudCBHcmFwaGljcyBmb3IgRGF0YSBBbmFseXNpc10oVXNlIFIhKShodHRwOi8vZ2dwbG90Mi5vcmcvYm9vay8pIAoqIFdpa2k6IGh0dHBzOi8vZ2l0aHViLmNvbS9oYWRsZXkvZ2dwbG90Mi93aWtpCiogYHBsb3RseWAgW2dpdGh1Yl0oaHR0cHM6Ly9naXRodWIuY29tL3JvcGVuc2NpL3Bsb3RseSkK